king
2024-04-29 f9816a4078fdba44115c69025d9982997f23b484
Merge branch 'develop'
227个文件已修改
21个文件已添加
12369 ■■■■ 已修改文件
public/manifest.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/main.scss 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/viewstyle.scss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/invoice.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/wx-icon.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/breadview/index.jsx 112 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/breadview/index.scss 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/index.jsx 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/index.scss 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/normalform/modalform/index.jsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/normalform/modalform/mkTable/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/normalform/modalform/mkTable/index.scss 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/tabview/index.jsx 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/tabview/index.scss 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/index.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/balcony/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/action.jsx 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/card.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/index.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/elementform/index.jsx 259 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/elementform/index.scss 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/formconfig.jsx 133 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.jsx 71 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.scss 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/index.jsx 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/index.scss 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardsimplecomponent/index.jsx 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardsimplecomponent/index.scss 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardsimplecomponent/options.jsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.jsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/options.jsx 125 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/double-data-card/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/double-data-card/options.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/doublecardcomponent/index.jsx 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/doublecardcomponent/index.scss 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/prop-card/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/prop-card/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/table-card/index.jsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/table-card/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/data-card/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/prop-card/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/actionform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/formconfig.jsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/simple-form/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/step-form/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/tab-form/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/normal-group/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/normal-group/options.jsx 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/index.jsx 479 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/index.scss 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/options.jsx 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/baseform/index.jsx 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/baseform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/callbackcustomscript/index.jsx 294 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/callbackcustomscript/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/customscript/index.jsx 284 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/customscript/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/index.jsx 690 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/invoice/verifycard/index.scss 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/voucher/index.jsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/voucher/index.scss 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/voucher/options.jsx 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/module/voucher/voucherTable/index.scss 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/dragsearch/card.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/options.jsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/actionform/index.jsx 168 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/actionform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/actionform/mkPrintTemps/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/formconfig.jsx 402 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/index.jsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/normalheader/index.jsx 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/normalheader/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/searchcomponent/dragsearch/card.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/searchcomponent/index.jsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/searchcomponent/index.scss 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/base-table/columns/editColumn/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/base-table/options.jsx 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/edit-table/columns/tableIn/customscript/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/edit-table/columns/tableIn/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/edit-table/options.jsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/editColumn/index.jsx 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/index.jsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/options.jsx 110 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/timeline/normal-timeline/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/index.jsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/index.scss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/options.jsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/customscript/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/index.jsx 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/settingform/index.jsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/utils.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/debug/index.jsx 62 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/header/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/card.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modalconfig/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modalconfig/index.scss 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modulecell/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modulesource/option.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/index.jsx 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/index.scss 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/options.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/header/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/basetable/index.jsx 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/commontable/index.jsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/calendar/index.jsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/balcony/index.jsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardcellList/index.jsx 227 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardcellList/index.scss 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.jsx 90 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.scss 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/double-data-card/index.jsx 50 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/double-data-card/index.scss 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/prop-card/index.jsx 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/table-card/index.jsx 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/table-card/index.scss 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/data-card/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/prop-card/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-G6/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-X6/index.jsx 92 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-X6/index.scss 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-X6/nodeupdate/index.jsx 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-X6/nodeupdate/index.scss 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-X6/nodeupdate/nodeform.jsx 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-bar-line/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-dashboard/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-pie/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-scatter/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/custom-chart/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/code/sand-box/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/editor/braft-editor/index.jsx 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/form/simple-form/index.jsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/form/step-form/index.jsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/form/tab-form/index.jsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/group/normal-group/index.jsx 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/group/normal-group/index.scss 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/iframe/index.jsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/iframe/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/interfaces/interItem/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/account/index.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/account/index.scss 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/invoice/index.jsx 757 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/invoice/index.scss 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/invoice/invoiceTable/index.jsx 420 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/invoice/invoiceTable/index.scss 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/invoice/subTable/index.jsx 250 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/invoice/subTable/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/index.jsx 301 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/index.scss 71 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/resetAttach/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/resetRemark/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/voucherTable/index.jsx 100 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/voucherTable/index.scss 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/normalTable/index.jsx 115 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/normalTable/index.scss 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/normalheader/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/tabtransfer/index.jsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/table/base-table/index.jsx 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/table/edit-table/index.jsx 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/table/edit-table/normalTable/index.jsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/table/edit-table/normalTable/index.scss 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/table/normal-table/index.jsx 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/timeline/normal-timeline/index.jsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/tree/antd-tree/index.jsx 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/tree/antd-tree/index.scss 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/index.jsx 99 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/popview/index.jsx 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/debugtable/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtable/index.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtabtable/index.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/treepage/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/changeuserbutton/index.jsx 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/editLine/index.jsx 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/excelInbutton/index.jsx 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/exceloutbutton/index.jsx 255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/exportPdf/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/funcMegvii/index.jsx 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/funczip/index.jsx 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/index.scss 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/newpagebutton/index.jsx 107 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/normalbutton/index.jsx 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/popupbutton/index.jsx 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/printbutton/index.jsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/shareLink/index.jsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/tabbutton/index.jsx 157 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/tabbutton/index.scss 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/automatic/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/flowFloat/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkDatePicker/index.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkNumberInput/index.jsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/normalTable/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/normalTable/index.scss 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/settingcomponent/index.jsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/tablenodes/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/advanceform/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/index.jsx 68 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/index.scss 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/mkDatePicker/index.jsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/settingform/index.jsx 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/actionform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/formconfig.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelin/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/customscript/index.jsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/datasource/index.jsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx 300 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/index.scss 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/otherform/index.jsx 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/otherform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/utils.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifymegvii/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyprint/index.jsx 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyprint/utils.jsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/dragsearch/card.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/customscript/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/formconfig.jsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/modalform/index.jsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/pasteform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/callbackcustomscript/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/customscript/index.jsx 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils-custom.js 63 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils-datamanage.js 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils.js 211 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appcheck/header/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/header/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/billprint/index.jsx 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/header/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/header/index.jsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/index.jsx 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/loginform.jsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mkiframe/index.jsx 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/rolemanage/header/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/systemfunc/header/index.jsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/systemfunc/sidemenu/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tabledesign/index.jsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/manifest.json
@@ -6,5 +6,5 @@
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff",
  "mk_version": "20240302"
  "mk_version": "20240401"
}
src/api/index.js
@@ -401,7 +401,7 @@
        this.getSystemConfig(param).then(res => {
          if (!res.status) {
            reject()
            reject(res.message)
            return
          }
@@ -441,7 +441,7 @@
          CacheUtils.updateIndexDBversion({version: res.app_version || '1.00', createDate: curTime})
          resolve()
          resolve(list)
        })
      }, () => {
        reject()
src/assets/css/main.scss
@@ -100,9 +100,15 @@
  color: unset;
}
// 重置按钮中文字与图标距离
.ant-btn > .anticon + span, .ant-btn > span + .anticon {
  margin-left: 5px;
// 重置按钮加载中样式
.ant-btn.ant-btn-loading .anticon:not(.anticon-loading) {
  transition: all 0.3s;
}
.ant-btn.ant-btn-loading >.anticon-loading + span .anticon, .ant-btn.ant-btn-loading >.anticon-loading + .anticon {
  width: 0;
  opacity: 0;
  margin-left: 0px!important;
  margin-right: 0px!important;
}
// 设置下拉搜索滚动条样式
@@ -762,4 +768,9 @@
  .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-checked):not(.ant-radio-button-wrapper-disabled):hover {
    color: var(--mk-sys-color);
  }
}
// 鼠标悬浮增加下划线
.mk-hover-underline:hover {
  text-decoration: underline!important;
}
src/assets/css/viewstyle.scss
@@ -175,8 +175,10 @@
    border-color: $color6;
  }
  // 下拉框
  .ant-select-selection:hover {
    border-color: $color5;
  .ant-select:not(.ant-select-disabled) {
    .ant-select-selection:hover {
      border-color: $color5;
    }
  }
  .ant-select-focused .ant-select-selection, .ant-select-selection:focus, .ant-select-selection:active {
    border-color: $color5;
@@ -187,7 +189,7 @@
    background-color: $color6;
  }
  // 输入框
  .ant-input:hover {
  .ant-input:not([disabled]):hover {
    border-color: $color5;
  }
  .ant-input:focus, .ant-input:active {
@@ -198,7 +200,7 @@
    border-color: $color5;
    box-shadow: 0 0 0 2px $color2;
  }
  .ant-input-number:hover, .ant-input-number:focus, .ant-input-number:active, .ant-input-number-input:hover, .ant-input-number-input:focus, .ant-input-number-input:active, .ant-input-number-focused:hover, .ant-input-number-focused:focus, .ant-input-number-focused:active {
  .ant-input-number:not(.ant-input-number-disabled):hover, .ant-input-number:focus, .ant-input-number:active, .ant-input-number-input:hover, .ant-input-number-input:focus, .ant-input-number-input:active, .ant-input-number-focused:hover, .ant-input-number-focused:focus, .ant-input-number-focused:active {
    border-color: $color5;
  }
  .ant-input-number:focus, .ant-input-number:active {
src/assets/img/invoice.png
src/assets/img/wx-icon.png
src/components/breadview/index.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import { BackTop, Breadcrumb, notification} from 'antd'
import { HomeOutlined, RightOutlined, RedoOutlined } from '@ant-design/icons'
import { BackTop, Breadcrumb, notification, Modal } from 'antd'
import { HomeOutlined, RightOutlined, RedoOutlined, LoadingOutlined } from '@ant-design/icons'
import moment from 'moment'
import 'moment/locale/zh-cn'
@@ -22,8 +22,38 @@
class BreadView extends Component {
  state = {
    tabview: null, // 标签
    hasNavBar: window.GLOB.navBar === 'linkage_navigation'
    tabview: null,
    hasNavBar: window.GLOB.navBar === 'linkage_navigation',
    visible: false
  }
  reloading = false
  UNSAFE_componentWillMount () {
    let home = {
      MenuID: 'home_page_id',
      MenuName: '首页',
      type: 'Home'
    }
    this.setState({tabview: home})
  }
  componentDidMount () {
    MKEmitter.addListener('modifyTabs', this.modifyTabs)
    if (window.GLOB.forcedUpdate) {
      MKEmitter.addListener('reloadTabs', this.reloadTabs)
    }
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('reloadTabs', this.reloadTabs)
    MKEmitter.removeListener('modifyTabs', this.modifyTabs)
  }
  refreshTabview = () => {
@@ -105,15 +135,6 @@
    }
  }
  UNSAFE_componentWillMount () {
    let home = {
      MenuID: 'home_page_id',
      MenuName: '首页',
      type: 'Home'
    }
    this.setState({tabview: home})
  }
  gotoHome = () => {
    let home = {
      MenuID: 'home_page_id',
@@ -134,22 +155,48 @@
    }
  }
  componentDidMount () {
    MKEmitter.addListener('modifyTabs', this.modifyTabs)
  }
  reloadTabs = () => {
    if (this.reloading) return
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('modifyTabs', this.modifyTabs)
    this.reloading = true
    let time = new Date().getTime()
    this.setState({visible: true})
    Api.getAppVersion(true).then((list) => {
      let _time = new Date().getTime()
      let delay = _time - time
      delay = delay < 3000 ? 3000 - delay : 0
      setTimeout(() => {
        this.setState({visible: false})
        this.reloading = false
        Modal.success({
          title: '更新成功。',
        })
        if (list && list.length && list.includes(this.state.tabview.MenuID)) {
          MKEmitter.emit('reloadMenuView', this.state.tabview.MenuID)
        }
      }, delay)
    }, (message) => {
      let _time = new Date().getTime()
      let delay = _time - time
      delay = delay < 3000 ? 3000 - delay : 0
      setTimeout(() => {
        this.setState({visible: false})
        this.reloading = false
        Modal.error({
          title: message || '系统配置更新失败!',
        })
      }, delay)
    })
  }
  render () {
    const { tabview, hasNavBar } = this.state
    const { tabview, hasNavBar, visible } = this.state
    return (
      <section id="mk-tabgroup-wrap" className="mk-breadview-wrap">
@@ -175,6 +222,21 @@
            </div>
          </div>
        </BackTop>
        <Modal
          visible={visible}
          width={400}
          closable={false}
          centered={true}
          footer={null}
          destroyOnClose
        >
          <div className="mk-menus-update">
            <div className="tip">
              系统更新中,请稍后
            </div>
            <LoadingOutlined />
          </div>
        </Modal>
      </section>
    )
  }
src/components/breadview/index.scss
@@ -46,4 +46,16 @@
.ant-message {
  top: 50px;
  z-index: 1080;
}
.mk-menus-update {
  text-align: center;
  font-size: 20px;
  color: #131313;
  .tip {
    margin-bottom: 10px;
  }
  .anticon {
    color: var(--mk-sys-color);
  }
}
src/components/header/index.jsx
@@ -10,6 +10,7 @@
import MKEmitter from '@/utils/events.js'
import Utils from '@/utils/utils.js'
import avatar from '@/assets/img/avatar.jpg'
import wxicon from '@/assets/img/wx-icon.png'
import './index.scss'
const { confirm } = Modal
@@ -24,6 +25,7 @@
    userName: '',
    fullName: '',
    logourl: window.GLOB.mainlogo,
    wxVisible: false,
    loginVisible: false,
    loginLoading: false,
    avatar: Utils.getrealurl(sessionStorage.getItem('avatar')),
@@ -336,6 +338,7 @@
                  trdItem.OpenType = PageParam.OpenType || 'newtab'
                  trdItem.hidden = PageParam.hidden || 'false'
                  trdItem.menuColor = PageParam.menuColor || ''
                  trdItem.urlFields = PageParam.urlFields || ''
                  if (trdItem.type === 'NewPage') {
                    trdItem.OpenType = 'newpage'
@@ -435,6 +438,8 @@
          sessionStorage.setItem('CloudAvatar', res.icon)
          sessionStorage.setItem('cloudDataM', res.dataM ? 'true' : '')
          sessionStorage.setItem('cloudRole_id', res.role_id || '')
          sessionStorage.setItem('CloudLogo', res.open_logo || '')
          let _url = window.location.href.split('#')[0] + 'cloud'
          if (param.remember) {
@@ -594,14 +599,6 @@
    })
  }
  wxnotice = () => {
    Modal.success({
      className: 'mk-wx-notice',
      title: <QrCode card={{qrWidth: 320, color: '#000000'}} value={window.GLOB.baseurl + 'mob/wxnotice.html?userid=' + sessionStorage.getItem('UserID') + '&loginuid=' + sessionStorage.getItem('LoginUID')}/>,
      okText: '关闭'
    })
  }
  changeToHome = () => {
    if (!['linkage', 'menu_board'].includes(window.GLOB.navBar)) return
@@ -628,7 +625,7 @@
        <Menu.Item key="verup" onClick={this.verup}>
          页面更新
        </Menu.Item>
        {window.GLOB.WXNotice ? <Menu.Item key="wxnotice" onClick={this.wxnotice}>
        {window.GLOB.WXNotice ? <Menu.Item key="wxnotice" onClick={() => this.setState({wxVisible: true})}>
          微信消息
        </Menu.Item> : null}
        {window.GLOB.appVersion ? <Menu.Item key="version" onClick={this.about}>
@@ -801,6 +798,26 @@
        </Modal>
        {/* 修改密码 */}
        <Resetpwd />
        {/* 微信消息 */}
        <Modal
          wrapClassName="mk-wx-sms-modal"
          visible={this.state.wxVisible}
          title="扫码关注  接收消息"
          width={400}
          centered={true}
          onCancel={() => this.setState({wxVisible: false})}
          footer={null}
          destroyOnClose
        >
          <div className="wx-sms-wrap">
            <QrCode card={{qrWidth: 200, color: '#000000'}} value={window.GLOB.baseurl + 'mob/wxnotice.html?userid=' + sessionStorage.getItem('UserID') + '&loginuid=' + sessionStorage.getItem('LoginUID')}/>
            <div className="tip">
              <img src={wxicon} alt=""/>
              <span>微信扫码</span>
              <span>关注公众号</span>
            </div>
          </div>
        </Modal>
      </header>
    )
  }
src/components/header/index.scss
@@ -276,17 +276,34 @@
  }
}
.mk-wx-notice {
  top: 100px!important;
  .anticon-check-circle {
    display: none;
  }
  .ant-modal-confirm-title {
.mk-wx-sms-modal {
  .ant-modal-title {
    text-align: center;
    min-height: 320px;
    font-size: 22px;
    font-weight: 500;
    color: #151515;
    margin-top: 15px;
    letter-spacing: 2px;
  }
  .ant-modal-confirm-btns {
    float: none!important;
  .wx-sms-wrap {
    text-align: center;
    .tip {
      margin: 15px;
      img {
        width: 24px;
        margin-right: 10px;
      }
      span {
        font-size: 16px;
        letter-spacing: 2px;
        font-weight: 500;
        color: #333;
      }
      span + span {
        color: #45B449;
        font-weight: bold;
      }
    }
  }
}
src/components/normalform/modalform/index.jsx
@@ -18,6 +18,7 @@
const MKCheckbox = asyncComponent(() => import('./mkCheckbox'))
const StyleInput = asyncComponent(() => import('./styleInput'))
const MKFileUpload = asyncComponent(() => import('@/tabviews/zshare/fileupload'))
const MkPrintTemps = asyncComponent(() => import('@/menu/components/share/actioncomponent/actionform/mkPrintTemps'))
const MKColor = asyncComponent(() => import('@/mob/colorsketch'))
// const MKColor = asyncComponent(() => import('@/tabviews/zshare/mutilform/mkColor'))
const MkEditIcon = asyncComponent(() => import('@/components/mkIcon'))
@@ -314,14 +315,22 @@
          </Col>
        )
        return
      } else if (item.type === 'printTemps') {
        content = <MkPrintTemps onChange={(val) => this.recordChange({[item.field]: val})}/>
      }
      if (!content) return
      if (item.help) {
        let help = null
        if (typeof(item.help) === 'function') {
          help = item.help(this.record)
        } else {
          help = <span style={{fontSize: '12px'}}>{item.help}</span>
        }
        fields.push(
          <Col span={item.span || 12} key={index}>
            <Form.Item label={label} help={<span style={{fontSize: '12px'}}>{item.help}</span>}>
            <Form.Item label={label} help={help}>
              {getFieldDecorator(item.field, {
                initialValue: item.initval,
                rules: item.rules
src/components/normalform/modalform/mkTable/index.jsx
@@ -352,6 +352,10 @@
        if (_options.length) {
          col.extends.forEach(n => {
            record[n.value] = _options.map(o => o[n.key]).join(' / ')
            if (n.mutilLabel && !record[n.mutilLabel]) {
              record[n.mutilLabel] = _options[_options.length - 1][n.key]
            }
          })
        }
      } else {
src/components/normalform/modalform/mkTable/index.scss
@@ -23,6 +23,23 @@
      top: 4px;
      white-space: nowrap;
    }
    .mini-color {
      .color-sketch-block {
        width: 80%;
        .color-sketch-block-box {
          width: 100%;
        }
        .color-sketch-value {
          display: none;
        }
      }
    }
    .mk-color-value {
      display: inline-block;
      width: 80%;
      height: 24px;
      box-shadow: 0 0 2px #b8b8b8;
    }
    .ant-select {
      width: 100%;
    }
src/components/tabview/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import { fromJS } from 'immutable'
import {Tabs, BackTop, notification} from 'antd'
import { RedoOutlined, CloseOutlined } from '@ant-design/icons'
import { Tabs, BackTop, notification, Modal } from 'antd'
import { RedoOutlined, CloseOutlined, LoadingOutlined } from '@ant-design/icons'
import moment from 'moment'
import 'moment/locale/zh-cn'
@@ -25,8 +25,10 @@
  state = {
    activeId: '',
    tabviews: [],
    iFrameHeight: 0,
    visible: false
  }
  reloading = false
  UNSAFE_componentWillMount () {
    if (!window.GLOB.mkHS) {
@@ -44,6 +46,10 @@
  componentDidMount () {
    MKEmitter.addListener('modifyTabs', this.modifyTabs)
    MKEmitter.addListener('closeTabView', this.closeTabView)
    if (window.GLOB.forcedUpdate) {
      MKEmitter.addListener('reloadTabs', this.reloadTabs)
    }
  }
  /**
@@ -54,6 +60,7 @@
      return
    }
    MKEmitter.removeListener('modifyTabs', this.modifyTabs)
    MKEmitter.removeListener('reloadTabs', this.reloadTabs)
    MKEmitter.removeListener('closeTabView', this.closeTabView)
  }
@@ -143,6 +150,53 @@
    } else {
      MKEmitter.emit('reloadMenuView', menu.MenuID)
    }
  }
  reloadTabs = () => {
    if (this.reloading) return
    this.reloading = true
    let time = new Date().getTime()
    this.setState({visible: true})
    Api.getAppVersion(true).then((list) => {
      let _time = new Date().getTime()
      let delay = _time - time
      delay = delay < 3000 ? 3000 - delay : 0
      setTimeout(() => {
        this.setState({visible: false})
        this.reloading = false
        Modal.success({
          title: '更新成功。',
        })
        if (list && list.length) {
          let _tabIds = this.state.tabviews.map(item => item.MenuID)
          list.forEach((m, i) => {
            if (_tabIds.includes(m)) {
              setTimeout(() => {
                MKEmitter.emit('reloadMenuView', m)
              }, i * 20)
            }
          })
        }
      }, delay)
    }, (message) => {
      let _time = new Date().getTime()
      let delay = _time - time
      delay = delay < 3000 ? 3000 - delay : 0
      setTimeout(() => {
        this.setState({visible: false})
        this.reloading = false
        Modal.error({
          title: message || '系统配置更新失败!',
        })
      }, delay)
    })
  }
  modifyTabs = (tab, fixed) => {
@@ -236,7 +290,7 @@
  }
  render () {
    const { tabviews, activeId } = this.state
    const { tabviews, activeId, visible } = this.state
    return (
      <section id="mk-tabgroup-wrap" className="mk-tabview-wrap">
@@ -273,6 +327,21 @@
            </Tabs>
          }
        </div>
        <Modal
          visible={visible}
          width={400}
          closable={false}
          centered={true}
          footer={null}
          destroyOnClose
        >
          <div className="mk-menus-update">
            <div className="tip">
              系统更新中,请稍后
            </div>
            <LoadingOutlined />
          </div>
        </Modal>
      </section>
    )
  }
src/components/tabview/index.scss
@@ -110,4 +110,16 @@
.ant-message {
  top: 50px;
  z-index: 1080;
}
.mk-menus-update {
  text-align: center;
  font-size: 20px;
  color: #131313;
  .tip {
    margin-bottom: 10px;
  }
  .anticon {
    color: var(--mk-sys-color);
  }
}
src/index.js
@@ -75,6 +75,13 @@
      GLOB.externalDatabase = null
    }
    if (config.probation && /^20\d{2}-\d{2}-\d{2}$/.test(config.probation) && new Date(config.probation).getTime() > new Date().getTime()) {
      GLOB.probation = true
    }
    if (config.forcedUpdate && /^20\d{2}-\d{2}-\d{2}$/.test(config.forcedUpdate) && new Date(config.forcedUpdate).getTime() > new Date().getTime()) {
      GLOB.forcedUpdate = true
    }
    // 只有业务系统才可以设置为正式系统
    if (GLOB.sysType === 'local' && (config.systemType === 'official' || config.systemType === 'production')) {
      if (!config.mainSystemApi) {
@@ -89,12 +96,6 @@
      }
      GLOB.systemType = 'production'
      if (config.probation && /^20\d{2}-\d{2}-\d{2}$/.test(config.probation) && new Date(config.probation).getTime() > new Date().getTime()) {
        GLOB.probation = true
      }
      if (config.forcedUpdate && /^20\d{2}-\d{2}-\d{2}$/.test(config.forcedUpdate) && new Date(config.forcedUpdate).getTime() > new Date().getTime()) {
        GLOB.forcedUpdate = true
      }
    } else if (GLOB.sysType === 'local') {
      GLOB.probation = true
      GLOB.debugger = true
@@ -245,6 +246,11 @@
      sessionStorage.setItem('system_mark', _mark)
    }
    if (config.mainkey && GLOB.sysType !== 'cloud' && config.mainkey !== options.cakey) {
      GLOB.localkey = GLOB.appkey
      GLOB.appkey = config.mainkey
    }
    let lang = localStorage.getItem(_href + 'lang') || (config.defaultLang !== 'en-US' ? 'zh-CN' : 'en-US')
    sessionStorage.setItem('lang', lang)
src/menu/components/card/balcony/index.scss
@@ -32,7 +32,7 @@
  }
  .card-item {
    overflow: hidden;
    // overflow: hidden;
    position: relative;
    background-color: #ffffff;
    background-position: center center;
src/menu/components/card/cardcellcomponent/dragaction/action.jsx
@@ -66,17 +66,17 @@
  } else if (card.show === 'icon') {
    btnElement = (<Button style={_style} type="link"><MkIcon type={card.icon}/>{warning}</Button>)
  } else if (card.show === 'link') {
    btnElement = (<Button style={_style} type="link">{card.label}{card.icon ? <MkIcon type={card.icon}/> : null}{warning}</Button>)
    btnElement = (<Button style={_style} type="link">{card.showName || card.label}{card.icon ? <MkIcon type={card.icon}/> : null}{warning}</Button>)
  } else {
    btnElement = (<Button style={_style}>{card.icon ? <MkIcon type={card.icon}/> : null}{card.label}{warning}</Button>)
    btnElement = (<Button style={_style}>{card.icon ? <MkIcon type={card.icon}/> : null}{card.showName || card.label}{warning}</Button>)
  }
  let _style_ = null
  let _style_ = {opacity: isDragging ? 0.3 : 1, ...card.wrapStyle}
  if (card.style && card.style.clear === 'left') {
    _style_ = {clear: 'left'}
    _style_.clear = 'left'
  } else if (card.style && card.style.clear === 'right') {
    _style_ = {float: 'right'}
    _style_.float = 'right'
  }
  let className = card.width || 0
@@ -105,9 +105,7 @@
      </div>
    } trigger="hover">
      <div ref={node => drag(drop(node))} style={_style_} className={'ant-col card-button-cell ant-col-' + className} onDoubleClick={(e) => {e.stopPropagation(); doubleClickCard(id)}}>
        <div style={{opacity: isDragging ? 0.3 : 1, ...card.wrapStyle}}>
          {btnElement}
        </div>
        {btnElement}
      </div>
    </Popover>
  )
src/menu/components/card/cardcellcomponent/dragaction/card.jsx
@@ -59,7 +59,7 @@
  let _style = {opacity: isDragging ? 0.3 : 1}
  
  if (card.style) {
  if (card.style && card.eleType !== 'tag') {
    _style = {...card.style, opacity: isDragging ? 0.3 : 1}
    _style = resetStyle(_style)
  }
@@ -184,11 +184,27 @@
        let _s = {fontSize: card.fixSize, color: card.fixColor, marginLeft: card.fixLeft, marginRight: card.fixRight}
        val = <><span style={_s}>{card.prefix || ''}</span>{card.formula}<span style={_s}>{card.postfix || ''}</span></>
      }
      return (
        <div className="ant-mk-text" style={{height: card.innerHeight || 'auto'}}>
          {val}
        </div>
      )
    } else if (card.eleType === 'tag') {
      let vals = []
      if (card.datatype === 'static') {
        vals = card.value.split(',').filter(Boolean)
      } else {
        vals = [card.field || '']
      }
      let _style = resetStyle(card.style)
      return (
        <div className="ant-mk-tag">
          {vals.map((val, index) => <span key={index} className="tag-item" style={_style}>{val}</span>)}
        </div>
      )
    } else if (card.eleType === 'color') {
      _style.overflow = 'hidden'
      let _bgstyle = {backgroundColor: card.value || '#1890ff'}
src/menu/components/card/cardcellcomponent/dragaction/index.jsx
@@ -140,6 +140,18 @@
          if (item.value === 'splitline') {
            newcard.width = 24
            newcard.color = '#EBE9E9'
          } else if (item.value === 'tag') {
            newcard.style = {
              backgroundColor: 'var(--mk-sys-color1)',
              color: 'var(--mk-sys-color)',
              borderColor: 'var(--mk-sys-color)',
              borderWidth: '1px',
              borderRadius: '4px',
              paddingLeft: '7px',
              paddingRight: '7px',
              marginRight: '8px',
              marginBottom: '8px',
            }
          } else if (item.value === 'slider') {
            newcard.width = 24
            newcard.color = '#1890ff'
src/menu/components/card/cardcellcomponent/dragaction/index.scss
@@ -4,6 +4,7 @@
    font-style: inherit;
    font-weight: inherit;
    text-decoration: inherit;
    font-family: inherit;
  }
  .ant-mk-text:not(.line1):not(.line) {
    word-break: break-word;
@@ -93,6 +94,12 @@
    border-left: 0;
    border-right: 0;
  }
  .ant-mk-tag {
    .tag-item {
      display: inline-block;
      vertical-align: top;
    }
  }
  .ant-mk-check {
    white-space: nowrap;
    overflow: hidden;
src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { fromJS } from 'immutable'
import { Form, Row, Col, Input, Select, Radio, Tooltip, InputNumber, Cascader, Popover } from 'antd'
import { Form, Row, Col, Input, Select, Radio, Tooltip, InputNumber, Cascader, Popover, message, AutoComplete } from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
import { formRule } from '@/utils/option.js'
@@ -10,7 +10,9 @@
const { TextArea } = Input
const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
const CodeMirror = asyncComponent(() => import('@/templates/zshare/codemirror'))
const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
const MKTable = asyncComponent(() => import('@/components/normalform/modalform/mkTable'))
const MkEditIcon = asyncComponent(() => import('@/components/mkIcon'))
const cardTypeOptions = {
@@ -25,8 +27,9 @@
  barcode: ['eleType', 'datatype', 'width', 'barHeight', 'displayValue', 'interval', 'noValue'],
  qrcode: ['eleType', 'datatype', 'width', 'qrWidth', 'color', 'url', 'noValue'],
  currentDate: ['eleType', 'width', 'dateFormat', 'prefix', 'postfix', 'fixStyle'],
  formula: ['eleType', 'width', 'height', 'prefix', 'postfix', 'eval', 'formula', 'noValue', 'fixStyle', 'alignItems'],
  formula: ['eleType', 'width', 'height', 'eval', 'formula', 'noValue'],
  color: ['eleType', 'datatype', 'width', 'lenWidRadio', 'noValue', 'copyable'],
  tag: ['eleType', 'datatype', 'width', 'noValue', 'signs'],
}
class ElementEditForm extends Component {
@@ -58,7 +61,7 @@
      formlist: formlist.map(item => {
        item.hidden = !_options.includes(item.key)
        if (item.key === 'field' || item.key === 'linkurl' || item.key === 'bgImage' || item.key === 'posterField') {
        if ((item.key === 'field' || item.key === 'linkurl' || item.key === 'bgImage' || item.key === 'posterField') && item.type === 'select') {
          item.options = []
          
          if (side === 'sub') {
@@ -112,26 +115,39 @@
              })
            })
          }
        } else if (item.key === 'value' && card.eleType === 'slider') {
          item.type = 'number'
          item.label = '值'
        } else if (item.key === 'formula') {
          item.fields = []
          config.columns.forEach(col => {
            item.fields.push(col.field)
          })
          if (config.subColumns) {
            config.subColumns.forEach(col => {
          if (side === 'sub') {
            if (config.subColumns) {
              config.subColumns.forEach(col => {
                item.fields.push(col.field)
              })
            }
          } else {
            config.columns.forEach(col => {
              item.fields.push(col.field)
            })
          }
          item.fields = item.fields.join(', ')
        } else if (item.key === 'value' && card.eleType === 'text') {
          item.type = 'textarea'
          item.label = '内容'
        } else if (item.key === 'value') {
          item.tooltip = ''
          if (card.eleType === 'slider') {
            item.type = 'number'
            item.label = '值'
          } else if (card.eleType === 'text') {
            item.type = 'textarea'
            item.label = '内容'
            item.tooltip = '文本类型,会替换内容中的@username@、@fullName@、@mk_city@、@appname@、@bid@、@month@、@week@、@day@'
          } else if (card.eleType === 'tag') {
            item.type = 'textarea'
            item.label = '内容'
            item.tooltip = '多个值请使用逗号(英文)分隔。'
          } else {
            item.type = 'text'
            item.label = '内容'
          }
        } else if (item.key === 'format') {
          if (card.eleType === 'text') {
            item.options = item.oriOptions.filter(op => !['percent', 'thdSeparator', 'abs'].includes(op.value))
@@ -142,7 +158,7 @@
          item.required = card.eleType !== 'qrcode'
        }
        if (item.key === 'linkurl') {
          item.type = card.link === 'dynamic' ? 'select' : 'textarea'
          item.type = card.link === 'dynamic' ? item.defType : 'textarea'
        }
        return item
@@ -153,7 +169,7 @@
  getOptions = () => {
    let _options = fromJS(cardTypeOptions[this.record.eleType]).toJS() // 选项列表
    
    if (['text', 'number', 'picture', 'slider', 'barcode', 'qrcode', 'video', 'color'].includes(this.record.eleType)) {
    if (['text', 'number', 'picture', 'slider', 'barcode', 'qrcode', 'video', 'color', 'tag'].includes(this.record.eleType)) {
      if (this.record.datatype === 'dynamic') {
        _options.push('field')
        if (this.record.eleType === 'number') {
@@ -210,8 +226,13 @@
      if (this.record.tipType === 'text') {
        _options.push('height')
      }
    } else if (this.record.eleType === 'formula' && this.record.eval === 'true') {
      _options.push('decimal')
    } else if (this.record.eleType === 'formula') {
      if (this.record.eval !== 'func') {
        _options.push('prefix', 'postfix', 'fixStyle', 'alignItems')
      }
      if (this.record.eval === 'true') {
        _options.push('decimal')
      }
    }
    if (_options.includes('fixStyle') && this.record.fixStyle === 'alone') {
      _options.push('fixSize', 'fixColor', 'fixLeft', 'fixRight')
@@ -245,7 +266,7 @@
        item.initVal = this.record[item.key]
        item.hidden = !_options.includes(item.key)
        if (item.key === 'field') {
        if (item.key === 'field' && item.type === 'select') {
          item.options = []
          if (side === 'sub') {
@@ -281,12 +302,18 @@
            })
          }
        } else if (item.key === 'value') {
          item.tooltip = ''
          if (value === 'slider') {
            item.type = 'number'
            item.label = '值'
          } else if (value === 'text') {
            item.type = 'textarea'
            item.label = '内容'
            item.tooltip = '文本类型,会替换内容中的@username@、@fullName@、@mk_city@、@appname@、@bid@、@month@、@week@、@day@'
          } else if (value === 'tag') {
            item.type = 'textarea'
            item.label = '内容'
            item.tooltip = '多个值请使用逗号(英文)分隔。'
          } else {
            item.type = 'text'
            item.label = '内容'
@@ -339,7 +366,7 @@
          item.hidden = !_options.includes(item.key)
          if (item.key === 'linkurl') {
            item.type = value === 'dynamic' ? 'select' : 'textarea'
            item.type = value === 'dynamic' ? item.defType : 'textarea'
          }
          return item
        })
@@ -374,8 +401,68 @@
      if (item.hidden || item.forbid) return
      if (item.type === 'text') {
        if (item.options && item.options.length > 0) {
          fields.push(
            <Col span={12} key={index}>
              <Form.Item label={item.label}>
                {getFieldDecorator(item.key, {
                  initialValue: item.initVal || '',
                  rules: [
                    {
                      required: !!item.required,
                      message: '请输入' + item.label + '!'
                    },
                    {
                      pattern: formRule.field.pattern,
                      message: formRule.field.message
                    },
                    {
                      max: formRule.input.max,
                      message: formRule.input.message
                    }
                  ]
                })(
                  <AutoComplete
                    dataSource={item.options.map((cell, i) => <AutoComplete.Option value={cell.value} key={i}>
                      {cell.value}
                    </AutoComplete.Option>)}
                    filterOption={(input, option) => option.props.children.indexOf(input) > -1}
                  >
                    <Input autoComplete="off" onPressEnter={this.handleSubmit} />
                  </AutoComplete>
                )}
              </Form.Item>
            </Col>
          )
        } else {
          fields.push(
            <Col span={12} key={index}>
              <Form.Item label={item.tooltip ?
                <Tooltip placement="topLeft" title={item.tooltip}>
                  <QuestionCircleOutlined className="mk-form-tip" />
                  {item.label}
                </Tooltip> : item.label
              }>
                {getFieldDecorator(item.key, {
                  initialValue: item.initVal || '',
                  rules: [
                    {
                      required: item.readonly ? false : !!item.required,
                      message: '请输入' + item.label + '!'
                    },
                    {
                      max: formRule.input.max,
                      message: formRule.input.message
                    }
                  ]
                })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
          )
        }
      } else if (item.type === 'textarea') {
        fields.push(
          <Col span={12} key={index}>
          <Col span={24} className="textarea" key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <QuestionCircleOutlined className="mk-form-tip" />
@@ -388,44 +475,35 @@
                  {
                    required: item.readonly ? false : !!item.required,
                    message: '请输入' + item.label + '!'
                  },
                  {
                    max: formRule.input.max,
                    message: formRule.input.message
                  }
                ]
              })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.handleSubmit} />)}
              })(<TextArea autoSize={{minRows: 2}} disabled={item.readonly} placeholder={item.placeholder || ''} />)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'textarea') {
        if (item.key === 'formula') {
      } else if (item.type === 'number') {
        if (item.help) {
          fields.push(
            <Col span={24} className="textarea" key={index}>
              <Form.Item label={item.tooltip ?
            <Col span={12} key={index}>
              <Form.Item help={item.help} label={item.tooltip ?
                <Tooltip placement="topLeft" title={item.tooltip}>
                  <QuestionCircleOutlined className="mk-form-tip" />
                  {item.label}
                </Tooltip> : item.label
              }>
                {getFieldDecorator(item.key, {
                  initialValue: item.initVal || '',
                  rules: [
                    {
                      required: item.readonly ? false : !!item.required,
                      message: '请输入' + item.label + '!'
                    }
                  ]
                })(<TextArea autoSize={{minRows: 2}} disabled={item.readonly} placeholder={item.placeholder || ''} />)}
                  initialValue: item.initVal,
                  rules: [{
                    required: item.readonly ? false : !!item.required,
                    message: '请输入' + item.label + '!'
                  }]
                })(<InputNumber min={item.min || 0} max={item.max || 10000} precision={item.precision || 0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
              <Popover overlayClassName="formula-fields" placement="topLeft" title="" content={<div>{item.fields}</div>} trigger="click">
                <span className="formula-icon">字段集</span>
              </Popover>
            </Col>
          )
        } else {
          fields.push(
            <Col span={24} className="textarea" key={index}>
            <Col span={12} key={index}>
              <Form.Item label={item.tooltip ?
                <Tooltip placement="topLeft" title={item.tooltip}>
                  <QuestionCircleOutlined className="mk-form-tip" />
@@ -433,37 +511,16 @@
                </Tooltip> : item.label
              }>
                {getFieldDecorator(item.key, {
                  initialValue: item.initVal || '',
                  rules: [
                    {
                      required: item.readonly ? false : !!item.required,
                      message: '请输入' + item.label + '!'
                    }
                  ]
                })(<TextArea autoSize={{minRows: 2}} disabled={item.readonly} placeholder={item.placeholder || ''} />)}
                  initialValue: item.initVal,
                  rules: [{
                    required: item.readonly ? false : !!item.required,
                    message: '请输入' + item.label + '!'
                  }]
                })(<InputNumber min={item.min || 0} max={item.max || 10000} precision={item.precision || 0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
          )
        }
      } else if (item.type === 'number') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item help={item.help || null} label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <QuestionCircleOutlined className="mk-form-tip" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [{
                  required: item.readonly ? false : !!item.required,
                  message: '请输入' + item.label + '!'
                }]
              })(<InputNumber min={item.min || 0} max={item.max || 10000} precision={item.precision || 0} onPressEnter={this.handleSubmit} />)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'select') { // 下拉搜索
        fields.push(
          <Col span={12} key={index}>
@@ -614,6 +671,55 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'table') {
        fields.push(
          <Col span={24} key={index} className="textarea">
            <Form.Item label={
              item.tooltip ? <Tooltip placement="topLeft" title={item.tooltip}>
                <QuestionCircleOutlined className="mk-form-tip" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal || '',
                rules: [
                  {
                    required: !!item.required,
                    message: '请输入' + item.label + '!'
                  }
                ]
              })(
                <MKTable columns={item.columns || []} actions={item.actions}/>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'codemirror') {
        fields.push(
          <Col span={24} key={index} className="textarea">
            <Form.Item label={
              <Tooltip placement="topLeft" overlayStyle={{width: 500, maxWidth: 500}} title={item.tooltip}>
                <QuestionCircleOutlined className="mk-form-tip" />
                {item.label}
              </Tooltip>
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal || '',
                rules: [
                  {
                    required: !!item.required,
                    message: '请输入' + item.label + '!'
                  }
                ]
              })(
                <CodeMirror mode="text/javascript"/>
              )}
            </Form.Item>
            <Popover overlayClassName="formula-fields" placement="topLeft" title="" content={<div>{item.fields}</div>} trigger="click">
              <span className="formula-icon">字段集</span>
            </Popover>
          </Col>
        )
      }
    })
    return fields
@@ -648,6 +754,19 @@
            })
          }
          if (values.width === 0) {
            let utype = {picture: '图片', video: '视频', slider: '进度条', splitline: '分割线', barcode: '条形码', qrcode: '二维码'}
            if (utype[values.eleType]) {
              message.warning(utype[values.eleType] + '元素宽度不可为0!')
              return
            }
          }
          if (values.eleType === 'tag' && values.signs && values.signs.length === 0) {
            values.signs = null
          }
          resolve(values)
        } else {
          reject(err)
src/menu/components/card/cardcellcomponent/elementform/index.scss
@@ -14,10 +14,13 @@
      .ant-form-item-control-wrapper {
        width: 85.8%;
      }
      .CodeMirror {
        height: 200px;
      }
      .formula-icon {
        position: absolute;
        bottom: 5px;
        top: -18px;
        right: 15px;
        cursor: pointer;
        font-size: 12px;
@@ -36,6 +39,10 @@
  .ant-form-explain, .ant-form-extra {
    font-size: 13px;
  }
  .ant-select-search__field__mirror {
    max-width: 100%;
    overflow: hidden;
  }
}
.formula-fields {
  z-index: 1200!important;
src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -1,3 +1,4 @@
import React from 'react'
import MenuUtils from '@/utils/utils-custom.js'
/**
@@ -20,6 +21,7 @@
    { value: 'qrcode', text: '二维码'},
    { value: 'currentDate', text: '当前时间'},
    { value: 'formula', text: '公式'},
    { value: 'tag', text: '标签'},
    { value: 'color', text: '颜色'},
  ]
@@ -52,7 +54,7 @@
    _options = [{ value: 'text', text: '文本'}]
  }
  let width = card.width || 12
  let width = card.width === undefined ? 12 : card.width
  if (/x/.test(card.width)) {
    width = +width.replace(/x/, '.5')
  }
@@ -60,6 +62,7 @@
  let linkTypes = [
    { value: 'tel', text: '电话' },
    { value: 'email', text: '邮箱' },
    { value: 'linkmenu', text: '关联菜单' },
    { value: 'download', text: '下载' },
    { value: 'other', text: '其他' }
  ]
@@ -89,13 +92,25 @@
      card.linkType = ''
      card.linkurl = ''
    }
  } else if (card.linkType === 'linkmenu') {
    if (appType !== 'mob' && appType !== 'pc') {
      card.link = ''
      card.linkType = ''
      card.linkurl = ''
      card.linkmenu = ''
  // } else if (card.linkType === 'linkmenu') {
  //   if (appType !== 'mob' && appType !== 'pc') {
  //     card.link = ''
  //     card.linkType = ''
  //     card.linkurl = ''
  //     card.linkmenu = ''
  //   }
  }
  let menulist = sessionStorage.getItem('fstMenuList')
  if (appType === '' && menulist) {
    try {
      menulist = JSON.parse(menulist)
    } catch (e) {
      menulist = []
    }
  } else {
    menulist = []
  }
  let appMenus = sessionStorage.getItem('appMenus')
@@ -110,6 +125,18 @@
      appMenus = []
    }
    appMenus.push({value: 'goback', text: '返回(上一页)'})
  }
  let fields = []
  if (cards.subtype === 'propcard' && cards.wrap.datatype === 'static') {
    if (cards.wrap.supModule && cards.wrap.supModule.length) {
      let cell = MenuUtils.getComponent(cards.wrap.supModule[cards.wrap.supModule.length - 1])
      if (cell && cell.columns) {
        fields = cell.columns.map(col => ({ value: col.field }))
      }
    } else {
      fields = cards.columns.map(col => ({ value: col.field }))
    }
  }
  let forms = [
@@ -145,12 +172,12 @@
      ]
    },
    {
      type: 'select',
      type: cards.subtype === 'propcard' && cards.wrap.datatype === 'static' ? 'text' : 'select',
      key: 'field',
      label: '字段',
      initVal: card.field || '',
      required: true,
      options: []
      options: fields
    },
    {
      type: 'icon',
@@ -165,7 +192,7 @@
      min: 0,
      label: '内容',
      initVal: card.value || '',
      tooltip: '文本类型,会替换内容中的@username@、@fullName@、@mk_city@、@appname@、@bid@、@month@、@week@、@day@',
      // tooltip: '文本类型,会替换内容中的@username@、@fullName@、@mk_city@、@appname@、@bid@、@month@、@week@、@day@',
      required: true
    },
    {
@@ -229,11 +256,12 @@
      required: true
    },
    {
      type: 'select',
      type: cards.subtype === 'propcard' && cards.wrap.datatype === 'static' ? 'text' : 'select',
      key: 'posterField',
      label: '预览地址',
      initVal: card.posterField || '',
      required: true
      required: true,
      options: fields
    },
    {
      type: 'radio',
@@ -301,6 +329,7 @@
        { value: 'YYYY-MM', text: 'YYYY-MM' },
        { value: 'YYYY-MM-DD HH:mm', text: 'YYYY-MM-DD HH:mm' },
        { value: 'YYYY-MM-DD HH:mm:ss', text: 'YYYY-MM-DD HH:mm:ss' },
        { value: 'YYYY年MM月DD日', text: 'YYYY年MM月DD日' },
      ]
    },
    {
@@ -427,12 +456,12 @@
    {
      type: 'number',
      key: 'width',
      min: 0.5,
      min: 0,
      max: 24,
      precision: 1,
      label: '元素宽度',
      initVal: width,
      tooltip: '栅格布局,每行等分为24列,可设置半列即.5。',
      tooltip: '栅格布局,每行等分为24列,可设置半列即.5。注:为0时宽度依据内容自适应(文本、数值、提示(图标)、当前时间、公式)。',
      required: true
    },
    {
@@ -446,14 +475,14 @@
      required: false
    },
    {
      type: 'select',
      type: cards.subtype === 'propcard' && cards.wrap.datatype === 'static' ? 'text' : 'select',
      key: 'bgImage',
      label: '动态背景',
      initVal: card.bgImage || '',
      tooltip: '绑定数据源字段,可根据返回值改变背景图。',
      required: false,
      allowClear: true,
      options: [],
      options: fields,
      forbid: isHeader
    },
    {
@@ -579,7 +608,7 @@
      key: 'link',
      label: '链接',
      initVal: card.link || '',
      tooltip: '动态地址为绑定字段值。',
      tooltip: '动态地址为绑定字段值。使用 动态-关联菜单 时,请在“链接地址”字段返回菜单ID。',
      required: false,
      options: [
        { value: '', text: '无' },
@@ -589,7 +618,7 @@
      forbid: isHeader
    },
    {
      type: linkTypes.length > 4 ? 'select' : 'radio',
      type: 'select',
      key: 'linkType',
      label: '链接类型',
      initVal: card.linkType || 'other',
@@ -617,13 +646,23 @@
      forbid: !['pc', 'mob'].includes(appType)
    },
    {
      type: 'select',
      type: 'cascader',
      key: 'linkmenu',
      label: '关联菜单',
      initVal: card.linkmenu || [],
      required: true,
      options: menulist,
      forbid: ['pc', 'mob'].includes(appType)
    },
    {
      type: cards.subtype === 'propcard' && cards.wrap.datatype === 'static' ? 'text' : 'select',
      defType: cards.subtype === 'propcard' && cards.wrap.datatype === 'static' ? 'text' : 'select',
      key: 'linkurl',
      label: '链接地址',
      initVal: card.linkurl || '',
      tooltip: ['pc', 'mob'].includes(appType) ? '当链接类型为“其他”,且链接地址以@menuid@开头时,其后内容将被视为菜单ID。' : '',
      required: true,
      options: []
      options: fields
    },
    {
      type: 'radio',
@@ -653,11 +692,11 @@
      }]
    },
    {
      type: 'textarea',
      type: 'codemirror',
      key: 'formula',
      label: '公式',
      initVal: card.formula || '',
      tooltip: '执行时会使用查询到的数据替换相应的字段,展示获得的结果,在不使用解析时换行符或空格会替换为页面元素。可使用JS的一些语法,如:三元表达式 @field1@ > @field2@ ? 0 : 1;Math对象,取绝对值 Math.abs(@field@)、四舍五入 Math.round(@field@)等',
      tooltip: '执行时会使用查询到的数据替换相应的字段,展示获得的结果,在不使用解析时换行符或空格会替换为页面元素。可使用JS的一些语法,如:三元表达式 @field1@ > @field2@ ? 0 : 1;Math对象,取绝对值 Math.abs(@field@)、四舍五入 Math.round(@field@)等。注:会替换公式中的@username@、@fullName@、@bid@。',
      placeholder: '例如:@price@ * @number@',
      required: true
    },
@@ -773,6 +812,56 @@
      options: [],
      forbid: !isHeader
    },
    {
      type: 'table',
      key: 'signs',
      label: '标记',
      initVal: card.signs || [],
      tooltip: '可依据标签内容设置不同样式。',
      required: false,
      actions: ['edit', 'del', 'add', 'move'],
      columns: [
        {
          title: '值',
          dataIndex: 'value',
          inputType: 'text',
          editable: true,
          required: true,
          unique: true,
          width: '20%'
        },
        {
          title: '背景',
          dataIndex: 'background',
          inputType: 'color',
          className: 'mini-color',
          editable: true,
          required: true,
          width: '20%',
          render: (text) => <span className="mk-color-value" style={{background: text}}></span>,
        },
        {
          title: '文字',
          dataIndex: 'color',
          inputType: 'color',
          className: 'mini-color',
          editable: true,
          required: true,
          width: '20%',
          render: (text) => <span className="mk-color-value" style={{background: text}}></span>,
        },
        {
          title: '边框',
          dataIndex: 'border',
          inputType: 'color',
          className: 'mini-color',
          editable: true,
          required: true,
          width: '20%',
          render: (text) => <span className="mk-color-value" style={{background: text}}></span>,
        },
      ]
    },
  ]
  return forms
src/menu/components/card/cardcellcomponent/index.jsx
@@ -24,11 +24,12 @@
class CardCellComponent extends Component {
  static propTpyes = {
    cards: PropTypes.object,         // 菜单配置信息
    cards: PropTypes.object,
    cardCell: PropTypes.object,
    side: PropTypes.string,
    elements: PropTypes.array,       // 元素集
    updateElement: PropTypes.func    // 菜单配置更新
    timestamp: PropTypes.any,
    elements: PropTypes.array,
    updateElement: PropTypes.func
  }
  state = {
@@ -74,7 +75,7 @@
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (this.props.side !== nextProps.side && nextProps.side) {
    if (this.props.timestamp !== nextProps.timestamp && nextProps.timestamp) {
      this.setState({
        elements: fromJS(nextProps.elements).toJS()
      })
@@ -150,9 +151,13 @@
      } else {
        options.push('width', 'minHeight', 'float')
        _style.minHeight = _style.minHeight || '28px'
        _style.textAlign = _style.textAlign || 'center'
      }
      if (element.wrapStyle) {
        _style.float = element.wrapStyle.textAlign || 'left'
      } else {
        _style.float = _style.float || 'center'
      }
    } else if (element.eleType === 'picture') {
      options = ['background', 'border', 'margin']
@@ -168,8 +173,12 @@
    } else if (element.eleType === 'splitline') {
      options = ['padding', 'margin']
    }
    if (['text', 'number', 'formula', 'currentDate'].includes(element.eleType)) {
      options.push('fontFamily')
      _style.fontFamily = _style.fontFamily ? _style.fontFamily.split(',') : []
    }
    if (element.eleType !== 'button') {
    if (element.eleType !== 'button' && element.eleType !== 'tag') {
      options.push('position')
    }
@@ -203,8 +212,16 @@
    let _card = fromJS(card).toJS()
    if (['text', 'number', 'formula', 'currentDate', 'sequence', 'icon'].includes(_card.eleType)) {
      _card.style = style
      _card.style = fromJS(style).toJS()
      let line = _card.height || null
      if (style.fontFamily) {
        if (style.fontFamily.length === 0) {
          delete _card.style.fontFamily
        } else {
          _card.style.fontFamily = style.fontFamily.join(',')
        }
      }
      if (['currentDate', 'sequence'].includes(_card.eleType) || (_card.eleType === 'icon' && _card.tipType !== 'text')) {
        line = 1
@@ -238,9 +255,17 @@
    } else if (_card.eleType === 'button') { // 拆分style
      _card.style = fromJS(style).toJS()
      if (style.float) {
      if (style.float === 'center') {
        delete _card.style.float
      } else if (style.float) {
        _card.wrapStyle = {textAlign: style.float}
        delete _card.style.float
      }
      if (style.textAlign === 'center') {
        delete _card.style.textAlign
      }
      if (style.minHeight === '28px') {
        delete _card.style.minHeight
      }
    } else if (_card.eleType === 'picture') {
      _card.style = style
@@ -322,7 +347,7 @@
      this.setState({
        actvisible: true,
        card: card,
        formlist: getBaseTableActionForm(card, functip, cards, usefulFields, modules)
        formlist: getBaseTableActionForm(card, functip, cards, usefulFields, modules, 'line')
      })
    } else {
      let anchors = MenuUtils.getAnchors(menu.components, cards.uuid) || []
@@ -330,7 +355,7 @@
      this.setState({
        actvisible: true,
        card: card,
        formlist: getActionForm(card, functip, cards, usefulFields, modules, anchors, side)
        formlist: getActionForm(card, functip, cards, usefulFields, modules, anchors, side, 'line')
      })
    }
  }
@@ -379,6 +404,9 @@
          if (!['text', 'number', 'icon'].includes(res.eleType)) {
            delete res.style.display
          }
          if (!['text', 'number', 'formula', 'currentDate'].includes(res.eleType)) {
            delete res.style.fontFamily
          }
          
          if (res.eleType === 'splitline' && (cell.eleType !== 'splitline' || cell.focus)) {
            res.style.paddingTop = '5px'
@@ -423,6 +451,21 @@
            delete res.style.paddingBottom
            delete res.style.paddingLeft
            delete res.style.paddingRight
          } else if (res.eleType === 'tag') {
            delete res.style.position
            if (cell.eleType !== 'tag') {
              res.style = {
                backgroundColor: 'var(--mk-sys-color1)',
                color: 'var(--mk-sys-color)',
                borderColor: 'var(--mk-sys-color)',
                borderWidth: '1px',
                borderRadius: '4px',
                paddingLeft: '7px',
                paddingRight: '7px',
                marginRight: '8px',
                marginBottom: '8px',
              }
            }
          }
          
          return res
@@ -447,6 +490,13 @@
    let color = { primary: '#1890ff', yellow: '#c49f47', orange: 'orange', danger: '#ff4d4f', green: '#26C281', dgreen: '#32c5d2', purple: '#8E44AD', cyan: '#13c2c2', gray: '#666666', default: 'rgba(0, 0, 0, 0.65)' }
    this.actionFormRef.handleConfirm().then(res => {
      if (res.width % 0.5) {
        res.width = parseInt(res.width / 0.5) * 0.5
      }
      if (res.width % 1) {
        res.width = (res.width + '').replace(/.5/, 'x')
      }
      let _elements = elements.map(cell => {
        if (cell.uuid === res.uuid) {
          res.eleType = cell.eleType || null
@@ -457,7 +507,8 @@
            if (cell.OpenType !== 'form') {
              res.style = {}
            }
          } else if (res.class !== cell.class || res.show !== cell.show || !res.style) {
          // } else if (res.class !== cell.class || res.show !== cell.show || !res.style) {
          } else if (res.class !== cell.class || !res.style) {
            if (res.class) {
              let cl = res.class.replace('border-', '')
              let style = {}
src/menu/components/card/cardcellcomponent/index.scss
@@ -9,6 +9,7 @@
  .card-button-cell {
    float: left;
    text-align: center;
    button {
      box-shadow: none;
    }
@@ -18,7 +19,7 @@
      background-position: center center;
      height: auto;
      min-height: 28px;
      text-align: center;
      text-align: inherit;
      border-width: 0;
      padding: 0;
      overflow: hidden;
@@ -40,6 +41,10 @@
      border-radius: 15px;
    }
  }
  .card-cell.ant-col-0 {
    display: block;
    float: left;
  }
  .card-cell:hover, .card-button-cell:hover {
    box-shadow: 0px 0px 2px #1890ff;
  }
src/menu/components/card/cardcomponent/index.jsx
@@ -33,6 +33,7 @@
    formlist: null,        // 设置表单信息
    elements: null,        // 编辑组
    side: 'front',
    timestamp: '',
    appType: sessionStorage.getItem('appType'),
    visible: false
  }
@@ -44,6 +45,7 @@
    const { card } = this.props
    this.setState({
      timestamp: new Date().getTime() + '',
      card: fromJS(card).toJS(),
      elements: fromJS(card.elements).toJS(),
    })
@@ -117,7 +119,7 @@
      _elements = fromJS(card.backElements).toJS()
    }
    this.setState({side: _side, elements: _elements})
    this.setState({side: _side, elements: _elements, timestamp: new Date().getTime() + ''})
  }
  
  addElement = () => {
@@ -215,9 +217,7 @@
          okText: '确定',
          cancelText: '取消',
          onOk() {
            that.setState({ card: _card, side: '', elements: fromJS(_card.elements).toJS() }, () => {
              that.setState({ side: 'front' })
            })
            that.setState({ card: _card, side: 'front', timestamp: new Date().getTime() + '', elements: fromJS(_card.elements).toJS() })
            that.props.updateElement(_card)
          },
          onCancel() {}
@@ -267,6 +267,7 @@
    if (side === 'back' && res.type === 'simple') {
      this.setState({
        side: 'front',
        timestamp: new Date().getTime() + '',
        elements: fromJS(_card.elements).toJS()
      })
    }
@@ -274,8 +275,121 @@
    this.props.updateElement(_card)
  }
  updateFields = (elements, resolve, type) => {
    const { card, side } = this.state
    let items = elements
    if (type === 'form') {
      items = []
      elements.forEach(item => {
        let cell = {
          uuid: Utils.getuuid(),
          eleType: 'text',
          height: 1,
          innerHeight: 21,
          prefix: item.label ? item.label + ': ' : '',
          datatype: item.field ? 'dynamic' : 'static',
          field: item.field || '',
          width: item.span || 12,
          value: '',
          style: {marginBottom: '15px'}
        }
        if (item.type === 'number') {
          cell.eleType = 'number'
          cell.decimal = typeof(item.decimal) === 'number' ? item.decimal : ''
        }
        items.push(cell)
      })
    }
    if (items.length === 0) {
      resolve({status: false, message: '元素不可为空!'})
      return
    }
    let _card = { ...card }
    let _card2 = { ...card }
    let pass = false
    if (side === 'back') {
      if (!_card.backElements || _card.backElements.length === 0) {
        _card.backElements = items
        pass = true
      } else {
        _card.backElements = items
        _card2.backElements = [...card.backElements, ...items]
      }
    } else {
      if (!_card.elements || _card.elements.length === 0) {
        _card.elements = items
        pass = true
      } else {
        _card.elements = items
        _card2.elements = [...card.elements, ...items]
      }
    }
    if (pass) {
      this.setState({
        card: _card,
        timestamp: new Date().getTime() + '',
        elements: side === 'back' ? fromJS(_card.backElements).toJS() : fromJS(_card.elements).toJS()
      })
      this.props.updateElement(_card)
    } else {
      const that = this
      const _modal = confirm({
        title: '卡片中已存在元素,请选择要进行的操作。',
        className: 'mk-confirm-modal',
        content: <div className="footer">
          <Button key="cancel" onClick={() => _modal.destroy()}>取消</Button>
          <Button key="replace" className="mk-border-purple" onClick={() => {
            that.setState({
              card: _card,
              timestamp: new Date().getTime() + '',
              elements: side === 'back' ? fromJS(_card.backElements).toJS() : fromJS(_card.elements).toJS()
            })
            that.props.updateElement(_card)
            _modal.destroy()
          }}>替换</Button>
          <Button key="confirm" className="mk-border-green" onClick={() => {
            that.setState({
              card: _card2,
              timestamp: new Date().getTime() + '',
              elements: side === 'back' ? fromJS(_card2.backElements).toJS() : fromJS(_card2.elements).toJS()
            })
            that.props.updateElement(_card2)
            _modal.destroy()
          }}>添加</Button>
        </div>
      })
    }
    resolve({status: true})
  }
  paste = (element, resolve) => {
    const { card } = this.state
    if (['simpleform', 'forms', 'formgroup', 'cardcell'].includes(element.copyType)) {
      if (element.copyType === 'simpleform') {
        this.updateFields(element.subcards[0].fields, resolve, 'form')
      } else if (element.copyType === 'forms') {
        this.updateFields(element.fields, resolve, 'form')
      } else if (element.copyType === 'formgroup') {
        this.updateFields(element.fields, resolve, 'form')
      } else {
        this.updateFields(element.elements, resolve)
      }
      return
    }
    let _uuid = Utils.getuuid()
    
@@ -343,7 +457,7 @@
  render() {
    const { cards } = this.props
    const { card, elements, side, visible } = this.state
    const { card, elements, side, visible, timestamp } = this.state
    let _style = {...card.style}
@@ -390,7 +504,7 @@
      <Col span={card.setting.width || 6} style={_style_}>
        <div className={'card-item ' + (card.setting.btnControl || '') + checkAll + tablerole} style={_style} onDoubleClick={(e) => {e.stopPropagation(); this.doubleClickCard()}} id={card.uuid}>
          <span className="circle-select"></span>
          <CardCellComponent cards={cards} cardCell={card} side={side} elements={elements} updateElement={this.updateCard}/>
          <CardCellComponent cards={cards} cardCell={card} side={side} timestamp={timestamp} elements={elements} updateElement={this.updateCard}/>
          <div className="card-control" onDoubleClick={(e) => e.stopPropagation()}>
            <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
              <div className="mk-popover-control">
@@ -400,7 +514,7 @@
                  <EditOutlined className="edit" title="编辑"/>
                </NormalForm>
                <CopyComponent type="cardcell" card={card}/>
                <PasteController options={['action', 'customCardElement']} updateConfig={this.paste} />
                <PasteController options={['action', 'customCardElement', 'simpleform', 'forms', 'formgroup', 'cardcell']} updateConfig={this.paste} />
                <FontColorsOutlined className="style" title="调整样式" onClick={this.changeStyle} />
                <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                  <div className="mk-popover-control">
src/menu/components/card/cardcomponent/index.scss
@@ -99,3 +99,16 @@
    display: inline;
  }
}
.mk-confirm-modal {
  .ant-modal-confirm-btns {
    display: none;
  }
  .footer {
    margin-top: 24px;
    text-align: right;
    .ant-btn {
      margin-left: 8px;
    }
  }
}
src/menu/components/card/cardsimplecomponent/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover } from 'antd'
import { Popover, Modal, Button } from 'antd'
import { PlusOutlined, PlusSquareOutlined, EditOutlined, ArrowLeftOutlined, ArrowRightOutlined, SwapOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -11,6 +11,8 @@
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const { confirm } = Modal
const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
const NodesWrap = asyncComponent(() => import('./node-wrap'))
@@ -30,7 +32,8 @@
  state = {
    card: null,            // 卡片信息
    formlist: null,        // 设置表单信息
    appType: sessionStorage.getItem('appType')
    appType: sessionStorage.getItem('appType'),
    timestamp: ''
  }
  /**
@@ -40,7 +43,8 @@
    const { card } = this.props
    this.setState({
      card: fromJS(card).toJS()
      card: fromJS(card).toJS(),
      timestamp: new Date().getTime() + ''
    })
  }
@@ -187,8 +191,99 @@
    this.props.updateElement(_card)
  }
  updateFields = (elements, resolve, type) => {
    const { card } = this.state
    let items = elements
    if (type === 'form') {
      items = []
      elements.forEach(item => {
        let cell = {
          uuid: Utils.getuuid(),
          eleType: 'text',
          height: 1,
          innerHeight: 21,
          prefix: item.label ? item.label + ': ' : '',
          datatype: item.field ? 'dynamic' : 'static',
          field: item.field || '',
          width: item.span || 12,
          value: '',
          style: {marginBottom: '15px'}
        }
        if (item.type === 'number') {
          cell.eleType = 'number'
          cell.decimal = typeof(item.decimal) === 'number' ? item.decimal : ''
        }
        items.push(cell)
      })
    }
    if (items.length === 0) {
      resolve({status: false, message: '元素不可为空!'})
      return
    }
    let _card = { ...card, elements: items }
    let _card2 = { ...card, elements: [...card.elements, ...items] }
    if (card.elements.length === 0) {
      this.setState({
        card: _card,
        timestamp: new Date().getTime() + ''
      })
      this.props.updateElement(_card)
    } else {
      const that = this
      const _modal = confirm({
        title: '卡片中已存在元素,请选择要进行的操作。',
        className: 'mk-confirm-modal',
        content: <div className="footer">
          <Button key="cancel" onClick={() => _modal.destroy()}>取消</Button>
          <Button key="replace" className="mk-border-purple" onClick={() => {
            that.setState({
              card: _card,
              timestamp: new Date().getTime() + ''
            })
            that.props.updateElement(_card)
            _modal.destroy()
          }}>替换</Button>
          <Button key="confirm" className="mk-border-green" onClick={() => {
            that.setState({
              card: _card2,
              timestamp: new Date().getTime() + ''
            })
            that.props.updateElement(_card2)
            _modal.destroy()
          }}>添加</Button>
        </div>
      })
    }
    resolve({status: true})
  }
  paste = (element, resolve) => {
    const { card } = this.state
    if (['simpleform', 'forms', 'formgroup', 'cardcell'].includes(element.copyType)) {
      if (element.copyType === 'simpleform') {
        this.updateFields(element.subcards[0].fields, resolve, 'form')
      } else if (element.copyType === 'forms') {
        this.updateFields(element.fields, resolve, 'form')
      } else if (element.copyType === 'formgroup') {
        this.updateFields(element.fields, resolve, 'form')
      } else {
        this.updateFields(element.elements, resolve)
      }
      return
    }
    let _uuid = Utils.getuuid()
    
@@ -208,7 +303,7 @@
  render() {
    const { cards } = this.props
    const { card } = this.state
    const { card, timestamp } = this.state
    let _style = {...card.style}
@@ -225,7 +320,7 @@
    return (
      <div className="card-item" style={_style}>
        <CardCellComponent cards={cards} cardCell={card} elements={card.elements} updateElement={this.updateCard}/>
        <CardCellComponent cards={cards} cardCell={card} timestamp={timestamp} elements={card.elements} updateElement={this.updateCard}/>
        <div className="card-control">
          <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
            <div className="mk-popover-control">
@@ -235,7 +330,7 @@
                <EditOutlined style={{color: '#1890ff'}} title="编辑"/>
              </NormalForm> : <NodesWrap card={card} updateMenus={this.updateNodes}/>}
              {cards.type !== 'timeline' ? <CopyComponent type="cardcell" card={card}/> : null}
              <PasteController options={['action', 'customCardElement']} updateConfig={this.paste} />
              <PasteController options={['action', 'customCardElement', 'simpleform', 'forms', 'formgroup', 'cardcell']} updateConfig={this.paste} />
              <FontColorsOutlined className="style" title="调整样式" onClick={this.changeStyle}/>
              {control ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                <div className="mk-popover-control">
src/menu/components/card/cardsimplecomponent/index.scss
@@ -12,4 +12,17 @@
    cursor: pointer;
    background: rgba(255, 255, 255, 0.55);
  }
}
.mk-confirm-modal {
  .ant-modal-confirm-btns {
    display: none;
  }
  .footer {
    margin-top: 24px;
    text-align: right;
    .ant-btn {
      margin-left: 8px;
    }
  }
}
src/menu/components/card/cardsimplecomponent/options.jsx
@@ -38,16 +38,18 @@
      field: 'condition',
      label: '显示条件',
      initval: setting.condition || 'false',
      tooltip: '当选择“有”时,只有符合条件的数据才会展示。',
      tooltip: '当选择“自定义”时,只有符合条件的数据才会展示。当选择“标题”时,只有首行才会展示',
      required: false,
      options: [
        {value: 'true', label: '有'},
        {value: 'false', label: '无'},
        {value: 'false', label: '始终显示'},
        {value: 'true', label: '自定义'},
        {value: 'title', label: '标题'},
      ],
      controlFields: [
        {field: 'controlField', values: ['true']},
        {field: 'controlType', values: ['true']},
        {field: 'controlValue', values: ['true']},
        {field: 'click', values: ['true', 'false']},
      ]
    },
    {
src/menu/components/card/data-card/index.jsx
@@ -370,6 +370,19 @@
  updateWrap = (res) => {
    const { card } = this.state
    if (res.interaction) {
      if (res.interaction.includes('scale')) {
        res.scale = 'true'
      }
      if (res.interaction.includes('parity')) {
        res.parity = 'true'
      }
      if (res.interaction.includes('hover')) {
        res.hover = 'true'
      }
      delete res.interaction
    }
    let _card = {...card, wrap: res}
    if (res.supNodes) {
@@ -538,7 +551,7 @@
            <PlusOutlined className="plus" title="添加卡片" onClick={() => this.addCard()}/>
            {appType !== 'mob' ? <PlusCircleOutlined className="plus" title="添加搜索" onClick={() => this.addSearch()}/> : null}
            <PlusSquareOutlined className="plus" title="添加按钮" onClick={() => this.addButton()}/>
            <NormalForm title="数据卡设置" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
            <NormalForm title="数据卡设置" width={850} update={this.updateWrap} getForms={this.getWrapForms}>
              <EditOutlined style={{color: '#1890ff'}} title="编辑"/>
            </NormalForm>
            <CopyComponent type="datacard" card={card}/>
src/menu/components/card/data-card/index.scss
@@ -20,7 +20,7 @@
  }
  .card-item {
    overflow: hidden;
    // overflow: hidden;
    position: relative;
    background-color: #ffffff;
    background-position: center center;
src/menu/components/card/data-card/options.jsx
@@ -59,6 +59,74 @@
    }
  }
  let interaction = []
  let interOptions = []
  if (wrap.scale === 'true') {
    interaction.push('scale')
  }
  if (wrap.parity === 'true') {
    interaction.push('parity')
  }
  if (wrap.hover === 'true') {
    interaction.push('hover')
  }
  if (subtype === 'datacard') {
    interOptions = [
      {value: 'parity', label: '奇偶异色'},
    ]
    if (appType !== 'mob') {
      interOptions.push(
        {value: 'hover', label: '悬浮变色'},
        {value: 'scale', label: '悬浮放大'}
      )
    }
  } else if (subtype === 'propcard' && appType !== 'mob') {
    interOptions = [
      {value: 'scale', label: '悬浮放大'}
    ]
  } else if (subtype === 'tablecard') {
    interOptions = [
      {value: 'parity', label: '奇偶异色'},
    ]
    if (appType !== 'mob') {
      interOptions.push(
        {value: 'hover', label: '悬浮变色'}
      )
    }
  }
  if (interaction.length && interOptions.length) {
    interaction = interaction.filter(m => interOptions.findIndex(cell => cell.value === m) > -1)
  }
  // let interItem = null
  // if (interOptions.length === 1) {
  //   interItem = {
  //     type: 'radio',
  //     field: interOptions[0].value,
  //     label: interOptions[0].label,
  //     initval: interaction.length === 1 ? 'true' : 'false',
  //     required: false,
  //     options: [
  //       {value: 'true', label: '启用'},
  //       {value: 'false', label: '禁用'}
  //     ]
  //   }
  // } else {
  //   interItem = {
  //     type: 'checkbox',
  //     field: 'interaction',
  //     label: '交互效果',
  //     initval: interaction,
  //     required: false,
  //     options: interOptions,
  //     forbid: interOptions.length === 0
  //   }
  // }
  const cardWrapForm = [
    {
      type: 'text',
@@ -306,31 +374,27 @@
      ],
      forbid: subtype !== 'datacard' || appType === 'mob'
    },
    // {
    //   type: 'radio',
    //   field: 'scale',
    //   label: '放大效果',
    //   initval: wrap.scale || 'false',
    //   tooltip: '鼠标悬浮于卡片上方时,卡片放大1.05倍。',
    //   required: false,
    //   options: [
    //     {value: 'false', label: '无'},
    //     {value: 'true', label: '有'},
    //   ],
    //   forbid: subtype === 'tablecard' || appType === 'mob'
    // },
    {
      type: 'radio',
      field: 'scale',
      label: '放大效果',
      initval: wrap.scale || 'false',
      tooltip: '鼠标悬浮于卡片上方时,卡片放大1.05倍。',
      type: 'checkbox',
      field: 'interaction',
      label: '交互效果',
      initval: interaction,
      required: false,
      options: [
        {value: 'false', label: '无'},
        {value: 'true', label: '有'},
      ],
      forbid: subtype === 'tablecard' || appType === 'mob'
    },
    {
      type: 'radio',
      field: 'parity',
      label: '奇偶背景',
      initval: wrap.parity || 'false',
      tooltip: '偶数行会添加背景色。',
      required: false,
      options: [
        {value: 'false', label: '无'},
        {value: 'true', label: '有'},
      ],
      forbid: subtype === 'propcard'
      options: interOptions,
      forbid: interOptions.length === 0
    },
    {
      type: 'radio',
@@ -610,6 +674,19 @@
    },
    {
      type: 'radio',
      field: 'searchBtn',
      label: '搜索按钮',
      initval: wrap.searchBtn || 'hidden',
      // tooltip: '启用搜索条件缓存后,在菜单刷新时搜索条件不变。',
      required: false,
      options: [
        {value: 'hidden', label: '隐藏'},
        {value: 'show', label: '显示'},
      ],
      forbid: appType === 'mob' || isprint,
    },
    {
      type: 'radio',
      field: 'shifting',
      label: '按钮偏移',
      initval: wrap.shifting || 'false',
@@ -669,7 +746,7 @@
          width: '20%'
        },
        {
          title: '菜单',
          title: '组件',
          dataIndex: 'nodes',
          inputType: 'cascader',
          editable: true,
src/menu/components/card/double-data-card/index.scss
@@ -20,7 +20,7 @@
  }
  .card-item, .card-item-wrap {
    overflow: hidden;
    // overflow: hidden;
    position: relative;
    background-color: #ffffff;
    background-position: center center;
src/menu/components/card/double-data-card/options.jsx
@@ -133,21 +133,21 @@
      tooltip: '数据卡右上角会显示收起开关。',
      required: false,
      options: [
        {value: 'false', label: '无'},
        {value: 'true', label: '有'},
        {value: 'true', label: '启用'},
        {value: 'false', label: '禁用'},
      ],
      forbid: appType === 'mob'
    },
    {
      type: 'radio',
      field: 'parity',
      label: '奇偶背景',
      label: '奇偶异色',
      initval: wrap.parity || 'false',
      tooltip: '偶数行会添加背景色。',
      tooltip: '子表偶数行会添加背景色。',
      required: false,
      options: [
        {value: 'false', label: '无'},
        {value: 'true', label: '有'},
        {value: 'true', label: '启用'},
        {value: 'false', label: '禁用'},
      ],
    },
    {
src/menu/components/card/doublecardcomponent/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Col } from 'antd'
import { Popover, Col, Modal, Button } from 'antd'
import { UpOutlined, PlusOutlined, PlusSquareOutlined, EditOutlined, ToolOutlined, FontColorsOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -11,6 +11,8 @@
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const { confirm } = Modal
const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
const CardCellComponent = asyncComponent(() => import('@/menu/components/card/cardcellcomponent'))
@@ -28,6 +30,7 @@
    card: null,            // 卡片信息,包括正反面
    appType: sessionStorage.getItem('appType'),
    visible: false,
    timestamp: '',
    side: ''
  }
@@ -38,7 +41,8 @@
    const { card } = this.props
    this.setState({
      card: fromJS(card).toJS()
      card: fromJS(card).toJS(),
      timestamp: new Date().getTime() + ''
    })
  }
@@ -208,8 +212,118 @@
    this.props.updateElement(_card)
  }
  updateFields = (elements, resolve, position, type) => {
    const { card } = this.state
    let items = elements
    if (type === 'form') {
      items = []
      elements.forEach(item => {
        let cell = {
          uuid: Utils.getuuid(),
          eleType: 'text',
          height: 1,
          innerHeight: 21,
          prefix: item.label ? item.label + ': ' : '',
          datatype: item.field ? 'dynamic' : 'static',
          field: item.field || '',
          width: item.span || 12,
          value: '',
          style: {marginBottom: '15px'}
        }
        if (item.type === 'number') {
          cell.eleType = 'number'
          cell.decimal = typeof(item.decimal) === 'number' ? item.decimal : ''
        }
        items.push(cell)
      })
    }
    if (items.length === 0) {
      resolve({status: false, message: '元素不可为空!'})
      return
    }
    let _card = { ...card }
    let _card2 = { ...card }
    let pass = false
    if (position === 'sub') {
      if (_card.backElements.length === 0) {
        _card.backElements = items
        pass = true
      } else {
        _card.backElements = items
        _card2.backElements = [...card.backElements, ...items]
      }
    } else {
      if (_card.elements.length === 0) {
        _card.elements = items
        pass = true
      } else {
        _card.elements = items
        _card2.elements = [...card.elements, ...items]
      }
    }
    if (pass) {
      this.setState({
        card: _card,
        timestamp: new Date().getTime() + ''
      })
      this.props.updateElement(_card)
    } else {
      const that = this
      const _modal = confirm({
        title: '卡片中已存在元素,请选择要进行的操作。',
        className: 'mk-confirm-modal',
        content: <div className="footer">
          <Button key="cancel" onClick={() => _modal.destroy()}>取消</Button>
          <Button key="replace" className="mk-border-purple" onClick={() => {
            that.setState({
              card: _card,
              timestamp: new Date().getTime() + ''
            })
            that.props.updateElement(_card)
            _modal.destroy()
          }}>替换</Button>
          <Button key="confirm" className="mk-border-green" onClick={() => {
            that.setState({
              card: _card2,
              timestamp: new Date().getTime() + ''
            })
            that.props.updateElement(_card2)
            _modal.destroy()
          }}>添加</Button>
        </div>
      })
    }
    resolve({status: true})
  }
  paste = (element, resolve, type) => {
    const { card } = this.state
    if (['simpleform', 'forms', 'formgroup', 'cardcell'].includes(element.copyType)) {
      if (element.copyType === 'simpleform') {
        this.updateFields(element.subcards[0].fields, resolve, type, 'form')
      } else if (element.copyType === 'forms') {
        this.updateFields(element.fields, resolve, type, 'form')
      } else if (element.copyType === 'formgroup') {
        this.updateFields(element.fields, resolve, type, 'form')
      } else {
        this.updateFields(element.elements, resolve, type)
      }
      return
    }
    let _uuid = Utils.getuuid()
    
@@ -238,7 +352,7 @@
  render() {
    const { cards } = this.props
    const { card } = this.state
    const { card, timestamp } = this.state
    let _style = {...card.style}
    let _backStyle = {...card.backStyle}
@@ -288,14 +402,14 @@
                    <EditOutlined className="edit" title="编辑"/>
                  </NormalForm>
                  <CopyComponent type="cardcell" card={{...card, backElements: []}}/>
                  <PasteController options={['action', 'customCardElement']} updateConfig={(element, resolve) => this.paste(element, resolve, 'main')} />
                  <PasteController options={['action', 'customCardElement', 'simpleform', 'forms', 'formgroup', 'cardcell']} updateConfig={(element, resolve) => this.paste(element, resolve, 'main')} />
                  <FontColorsOutlined className="style" title="调整样式" onClick={() => this.changeStyle()} />
                </div>
              } trigger="hover">
                <ToolOutlined />
              </Popover>
            </div>
            <CardCellComponent cards={cards} cardCell={card} side="main" elements={card.elements} updateElement={(elements, btn) => this.updateCard(elements, btn)}/>
            <CardCellComponent cards={cards} cardCell={card} side="main" timestamp={timestamp} elements={card.elements} updateElement={(elements, btn) => this.updateCard(elements, btn)}/>
          </div>
          <Col span={card.backSetting.width || 24}>
            <div className={'card-item ' + (card.backSetting.btnControl || '')} style={_backStyle} onDoubleClick={(e) => {e.stopPropagation(); this.doubleClickCard('sub')}} id={card.uuid}>
@@ -308,14 +422,14 @@
                      <EditOutlined className="edit" title="编辑"/>
                    </NormalForm>
                    <CopyComponent type="cardcell" card={{...card, elements: card.backElements, backElements: []}}/>
                    <PasteController options={['action', 'customCardElement']} updateConfig={(element, resolve) => this.paste(element, resolve, 'sub')} />
                    <PasteController options={['action', 'customCardElement', 'simpleform', 'forms', 'formgroup', 'cardcell']} updateConfig={(element, resolve) => this.paste(element, resolve, 'sub')} />
                    <FontColorsOutlined className="style" title="调整样式" onClick={() => this.changeStyle('sub')} />
                  </div>
                } trigger="hover">
                  <ToolOutlined />
                </Popover>
              </div>
              <CardCellComponent cards={cards} cardCell={card} side="sub" elements={card.backElements} updateElement={(elements, btn) => this.updateCard(elements, btn, 'sub')}/>
              <CardCellComponent cards={cards} cardCell={card} side="sub" timestamp={timestamp} elements={card.backElements} updateElement={(elements, btn) => this.updateCard(elements, btn, 'sub')}/>
            </div>
          </Col>
        </div>
src/menu/components/card/doublecardcomponent/index.scss
@@ -117,3 +117,16 @@
    display: inline-block;
  }
}
.mk-confirm-modal {
  .ant-modal-confirm-btns {
    display: none;
  }
  .footer {
    margin-top: 24px;
    text-align: right;
    .ant-btn {
      margin-left: 8px;
    }
  }
}
src/menu/components/card/prop-card/index.jsx
@@ -315,6 +315,13 @@
  updateWrap = (res) => {
    const { card } = this.state
    if (res.interaction) {
      if (res.interaction.includes('scale')) {
        res.scale = 'true'
      }
      delete res.interaction
    }
    let _card = {...card, wrap: res}
    if (res.datatype === 'static') {
src/menu/components/card/prop-card/index.scss
@@ -19,7 +19,7 @@
  }
  .card-item {
    overflow: hidden;
    // overflow: hidden;
    position: relative;
    background-color: #ffffff;
    background-position: center center;
src/menu/components/card/table-card/index.jsx
@@ -271,6 +271,15 @@
  }
  updateWrap = (res) => {
    if (res.interaction) {
      if (res.interaction.includes('parity')) {
        res.parity = 'true'
      }
      if (res.interaction.includes('hover')) {
        res.hover = 'true'
      }
      delete res.interaction
    }
    this.updateComponent({...this.state.card, wrap: res})
  }
src/menu/components/card/table-card/index.scss
@@ -63,6 +63,7 @@
  }
  .ant-pagination {
    text-align: right;
    margin-top: 10px;
  }
  .mk-more {
    text-align: center;
src/menu/components/carousel/data-card/index.scss
@@ -19,7 +19,7 @@
  }
  .card-item {
    overflow: hidden;
    // overflow: hidden;
    position: relative;
    background-position: center center;
    background-repeat: no-repeat;
src/menu/components/carousel/prop-card/index.scss
@@ -19,7 +19,7 @@
  }
  .card-item {
    overflow: hidden;
    // overflow: hidden;
    position: relative;
    background-position: center center;
    background-repeat: no-repeat;
src/menu/components/form/formaction/actionform/index.jsx
@@ -57,7 +57,7 @@
    } else if (this.record.type === 'close' || this.record.type === 'reset') {
      shows = ['typeName', 'label']
    } else {
      shows = ['typeName', 'label', 'intertype', 'Ot', 'execSuccess', 'syncComponent', 'anchors', 'linkmenu', 'output', 'reload', 'preButton', 'formCache'] // 选项列表
      shows = ['typeName', 'label', 'intertype', 'Ot', 'execSuccess', 'syncComponent', 'anchors', 'linkmenu', 'output', 'reload', 'preButton'] // 选项列表
      if (this.record.execSuccess === 'never') {
        shows.push('resetForms')
src/menu/components/form/formaction/formconfig.jsx
@@ -539,21 +539,6 @@
        value: 'true',
        text: '刷新'
      }]
    },
    // {
    //   type: 'radio',
    //   key: 'formCache',
    //   label: '表单缓存',
    //   initVal: card.formCache || 'false',
    //   tooltip: '主要用于数据修改后,更新相关表单的选项,清空缓存后表单再次打开时数据会重新加载。',
    //   required: false,
    //   options: [{
    //     value: 'false',
    //     text: '不清空'
    //   }, {
    //     value: 'clear',
    //     text: '清空'
    //   }]
    // },
    }
  ]
}
src/menu/components/form/simple-form/index.jsx
@@ -375,7 +375,7 @@
        })
      }
      if (item.type === 'switch' || item.type === 'check') {
      if (['switch', 'check', 'popSelect'].includes(item.type)) {
        _linksupFields.push({
          field: item.field,
          label: item.label
src/menu/components/form/step-form/index.jsx
@@ -477,7 +477,7 @@
        })
      }
      if (item.type === 'switch' || item.type === 'check') {
      if (['switch', 'check', 'popSelect'].includes(item.type)) {
        _linksupFields.push({
          field: item.field,
          label: item.label
src/menu/components/form/tab-form/index.jsx
@@ -482,7 +482,7 @@
        })
      }
      if (item.type === 'switch' || item.type === 'check') {
      if (['switch', 'check', 'popSelect'].includes(item.type)) {
        _linksupFields.push({
          field: item.field,
          label: item.label
src/menu/components/group/normal-group/index.scss
@@ -1,7 +1,7 @@
.menu-group-edit-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  // background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
src/menu/components/group/normal-group/options.jsx
@@ -122,6 +122,34 @@
      ]
    },
    {
      type: 'radio',
      field: 'mergeAble',
      label: '展开/收起',
      initval: setting.mergeAble || 'false',
      tooltip: '启用时,组件右上角将出现展开/收起的图标,可将当前组件展开或收起。',
      required: false,
      options: [
        {value: 'true', label: '启用'},
        {value: 'false', label: '禁用'},
      ],
      controlFields: [
        {field: 'ctrlNumber', values: ['true']},
      ],
      forbid: appType === 'mob'
    },
    {
      type: 'number',
      field: 'ctrlNumber',
      label: '控制数',
      initval: setting.ctrlNumber || 1,
      tooltip: '当组件收起时,其后需要展开的组件数。',
      min: 1,
      max: 5,
      precision: 0,
      required: true,
      forbid: appType === 'mob'
    },
    {
      type: 'multiselect',
      field: 'blacklist',
      label: '黑名单',
src/menu/components/module/invoice/index.jsx
New file
@@ -0,0 +1,479 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Popover, Button, Modal } from 'antd'
import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons'
import moment from 'moment'
import Utils from '@/utils/utils.js'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { getTables, checkComponent } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import VerifyCard from './verifycard'
import getWrapForm from './options'
import './index.scss'
const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
class Invoice extends Component {
  static propTpyes = {
    card: PropTypes.object,
    deletecomponent: PropTypes.func,
    updateConfig: PropTypes.func,
  }
  state = {
    card: null,
    date: moment().format('YYYY年MM月'),
    btn: null
  }
  UNSAFE_componentWillMount () {
    const { card } = this.props
    if (card.isNew) {
      let _card = {
        uuid: card.uuid,
        type: card.type,
        format: 'object',   // 组件属性 - 数据格式
        pageable: false,    // 组件属性 - 是否可分页
        switchable: false,  // 组件属性 - 数据是否可切换
        width: card.width || 24,
        name: '发票',
        subtype: card.subtype,
        wrap: { name: '发票', width: card.width || 24, datatype: 'static' },
        style: { paddingLeft: '20px', paddingRight: '20px', paddingTop: '10px', paddingBottom: '10px' },
        setting: { interType: 'system', primaryKey: 'ID' },
        columns: [],
        scripts: [],
        buyer: {
          pageable: true,
          format: 'array',
          subtype: 'invTable',
          setting: { interType: 'system' },
          columns: [],
          scripts: [],
          search: [
            {field: 'from_to_name', label: '企业名称', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()},
            {field: 'from_to_tax_no', label: '企业税号', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()},
          ],
        },
        detail: {
          pageable: true,
          format: 'array',
          subtype: 'invTable',
          setting: { interType: 'system' },
          columns: [],
          scripts: [],
          search: [
            {field: 'productname', label: '商品名称', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()},
            {field: 'productcode', label: '商品编码', initval: '', type: 'text', match: 'like', uuid: Utils.getuuid()},
          ],
        },
        billSaveBtn: {type: 'billsave', intertype: 'system', label: '保存单据'},
        billOutBtn: {type: 'billout', intertype: 'custom', label: '提交开票', procMode: 'system'},
      }
      let buys = [
        ['企业名称', 'from_to_name'],
        ['企业税号', 'from_to_tax_no'],
        ['地址', 'from_to_addr', 'Nvarchar(100)'],
        ['电话', 'from_to_tel'],
        ['开户行', 'from_to_bank_name'],
        ['银行账号', 'from_to_account_no'],
        ['手机号', 'from_to_mob'],
        ['邮箱', 'from_to_email'],
        ['企业编码', 'from_to_code']
      ]
      buys.forEach((cell, index) => {
        _card.buyer.columns.push({$index: index + 1, datatype: 'Nvarchar(50)', field: cell[1], label: cell[0], uuid: Utils.getuuid()})
      })
      let details = [
        ['id', 'id'],
        ['商品编码', 'productcode'],
        ['商品名称', 'productname'],
        ['规格型号', 'spec'],
        ['描述', 'Description'],
        ['计量单位', 'unit'],
        ['单价', 'unitprice', 'Decimal(18,10)'],
        ['税务分类编码', 'tax_classify_code'],
        ['税务分类名称', 'tax_classify_name'],
        // ['税率', 'tax_rate', 'Decimal(18,2)'],
        ['一般纳税人税率', 'general_tax_rate', 'Decimal(18,2)'],
        ['小规模纳税人征收率', 'small_tax_rate', 'Decimal(18,2)'],
        ['是否享受优惠政策', 'free_tax_mark'],
        ['优惠政策类型', 'vat_special_management'],
      ]
      details.forEach((cell, index) => {
        _card.detail.columns.push({$index: index + 1, datatype: cell[2] || 'Nvarchar(50)', field: cell[1], label: cell[0], uuid: Utils.getuuid()})
      })
      let cols = [
        ['ID', 'ID'],
        ['开票申请单号', 'io'],
        ['发票种类', 'invoice_type'],
        ['购买方名称', 'from_to_name'],
        ['购买方税号', 'from_to_tax_no'],
        ['购买方地址', 'from_to_addr', 'Nvarchar(100)'],
        ['购买方电话', 'from_to_tel'],
        ['购买方开户行', 'from_to_bank_name'],
        ['购买方银行账号', 'from_to_account_no'],
        ['购买方手机号', 'from_to_mob'],
        ['购买方邮箱', 'from_to_email'],
        ['购买方编码', 'from_to_code'],
        ['销售方名称', 'orgname'],
        ['销售方税号', 'tax_no'],
        ['销售方地址', 'addr', 'Nvarchar(100)'],
        ['销售方电话', 'tel'],
        ['销售方开户行', 'bank_name'],
        ['销售方银行账号', 'account_no'],
        ['备注', 'remark', 'Nvarchar(512)'],
        ['收款人', 'payee'],
        ['复核人', 'reviewer'],
        ['开票人', 'drawer'],
        ['行号', 'invoice_lp'],
        ['商品编码', 'productcode'],
        ['商品名称', 'productname'],
        ['规格型号', 'spec'],
        ['计量单位', 'unit'],
        ['数量', 'bill_count', 'Decimal(18,10)'],
        ['单价', 'unitprice', 'Decimal(18,10)'],
        ['金额', 'amount_line', 'Decimal(18,2)'],
        ['税务分类编码', 'tax_classify_code'],
        ['税务分类名称', 'tax_classify_name'],
        ['税率', 'tax_rate', 'Decimal(18,2)'],
        ['税额', 'tax_amount', 'Decimal(18,2)'],
      ]
      cols.forEach((cell, index) => {
        _card.columns.push({$index: index + 1, datatype: cell[2] || 'Nvarchar(50)', field: cell[1], label: cell[0], uuid: Utils.getuuid()})
      })
      this.updateComponent(_card)
    } else {
      let _card = fromJS(card).toJS()
      this.setState({
        card: _card
      })
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  /**
   * @description 卡片行外层信息更新(数据源,样式等)
   */
  updateComponent = (card) => {
    card.width = card.wrap.width
    card.name = card.wrap.name
    if (window.GLOB.styling && card.errors) { // 样式修改时不做筛查
      this.setState({
        card: card
      })
      this.props.updateConfig(card)
      return
    }
    card.$c_ds = card.wrap.datatype === 'dynamic'
    card.errors = checkComponent(card)
    if (card.errors.length === 0) {
      if (card.buyer.setting.interType === 'system' && card.buyer.setting.execute !== 'false' && !card.buyer.setting.dataresource) {
        card.errors.push({ level: 0, detail: '-购买方 未设置数据源!'})
      } else if (card.buyer.setting.interType === 'system' && card.buyer.setting.execute === 'false' && card.buyer.scripts.filter(script => script.status !== 'false').length === 0) {
        card.errors.push({ level: 0, detail: '-购买方 数据源中无可用脚本!'})
      }
    }
    if (card.errors.length === 0) {
      if (card.detail.setting.interType === 'system' && card.detail.setting.execute !== 'false' && !card.detail.setting.dataresource) {
        card.errors.push({ level: 0, detail: '-明细 未设置数据源!'})
      } else if (card.detail.setting.interType === 'system' && card.detail.setting.execute === 'false' && card.detail.scripts.filter(script => script.status !== 'false').length === 0) {
        card.errors.push({ level: 0, detail: '-明细 数据源中无可用脚本!'})
      }
    }
    if (!card.billSaveBtn.scripts || card.billSaveBtn.scripts.length === 0) {
      card.errors.push({ level: 0, detail: '未添加单据保存脚本!'})
    // } else if (!card.billOutBtn.scripts || card.billOutBtn.scripts.length === 0) {
    //   card.errors.push({ level: 0, detail: '未添加提交开票前置脚本!'})
    // } else if (card.billSaveBtn.cbScripts.length === 0) {
    //   card.errors.push({ level: 0, detail: '未添加提交开票回调脚本!'})
    }
    if (card.errors.length === 0) {
      card.$tables = getTables(card)
      card.$tables = [...card.$tables, ...getTables(card.buyer)]
      card.$tables = [...card.$tables, ...getTables(card.detail)]
    }
    this.setState({
      card: card
    })
    this.props.updateConfig(card)
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
  }
  getStyle = (style) => {
    let _card = {...this.state.card, style}
    this.updateComponent(_card)
  }
  getWrapForms = () => {
    const { card } = this.state
    return getWrapForm(card.wrap)
  }
  updateWrap = (res) => {
    this.updateComponent({...this.state.card, wrap: res})
  }
  verifySubmit = () => {
    this.verifyRef.handleConfirm().then(res => {
      if (res.type === 'billout') {
        this.updateComponent({...this.state.card, billOutBtn: res})
      } else {
        this.updateComponent({...this.state.card, billSaveBtn: res})
      }
      this.setState({ btn: null })
    })
  }
  render() {
    const { card, date, btn } = this.state
    let style = {...card.style}
    if (card.wrap.invColor) {
      style['--inv-color'] = card.wrap.invColor
    }
    return (
      <div className="menu-invoice-box" style={style} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <NormalForm title="基本设置" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
              <EditOutlined style={{color: '#1890ff'}} title="编辑"/>
            </NormalForm>
            <FontColorsOutlined className="style" title="调整样式" onClick={this.changeStyle}/>
            <DeleteOutlined className="close" title="删除组件" onClick={() => this.props.deletecomponent(card.uuid)} />
            {card.wrap.datatype === 'dynamic' ? <SettingComponent config={card} updateConfig={this.updateComponent} /> : <SettingOutlined style={{color: '#eeeeee', cursor: 'not-allowed'}}/>}
          </div>
        } trigger="hover">
          <ToolOutlined />
        </Popover>
        <div className="inv-action">
          <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
            <div className="mk-popover-control">
              <EditOutlined style={{color: '#1890ff'}} onClick={() => this.setState({btn: card.billSaveBtn})} title="编辑"/>
            </div>
          } trigger="hover">
            <Button className="mk-bill">保存单据</Button>
          </Popover>
          <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
            <div className="mk-popover-control">
              <EditOutlined style={{color: '#1890ff'}} onClick={() => this.setState({btn: card.billOutBtn})} title="编辑"/>
            </div>
          } trigger="hover">
            <Button className="mk-submit">提交开票</Button>
          </Popover>
        </div>
        <div className="inv-header">
          <div className="mk-select">请选择发票种类</div>
          <div className="date">开票日期:{date}</div>
        </div>
        <div className="inv-body">
          <div className="inv-main-content">
            <div className="inv-buyer">
              <div className="inv-label">购买方</div>
              <div className="inv-content">
                <div className="mk-input">
                  <label>名称:</label>
                  <span className="content">请输入购买方名称</span>
                  <span className="extra">
                    <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                      <div className="mk-popover-control">
                        <SettingComponent config={card.buyer} updateConfig={(res) => this.updateComponent({...card, buyer: res})} />
                      </div>
                    } trigger="hover">
                      <EllipsisOutlined />
                    </Popover>
                  </span>
                </div>
                <div className="mk-input">
                  <label>纳税人识别号:</label>
                  <span className="content">请输入购买方纳税人识别号</span>
                </div>
                <div className="mk-input">
                  <label>地址、电话:</label>
                  <span className="content">请输入购买方地址</span>
                  <span className="content">请输入购买方电话</span>
                </div>
                <div className="mk-input">
                  <label>开户行及账号:</label>
                  <span className="content">请输入购买方开户行</span>
                  <span className="content">请输入购买方账号</span>
                </div>
              </div>
            </div>
            <div className="inv-notice">
              <div className="inv-label">通知到</div>
              <div className="inv-content">
                <div className="mk-input">
                  <label>手机号:</label>
                  <span className="content">请输入购买方手机号</span>
                </div>
                <div className="mk-input">
                  <label>邮箱:</label>
                  <span className="content">请输入购买方邮箱</span>
                </div>
              </div>
            </div>
          </div>
          <div className="inv-details">
            <div className="detail-wrap">
              <div className="mk-th">
                <div className="mk-td">货物或应税劳务、服务名称</div>
                <div className="mk-td">规格型号</div>
                <div className="mk-td">单位</div>
                <div className="mk-td">数量</div>
                <div className="mk-td">单价(含税)</div>
                <div className="mk-td">金额(含税)</div>
                <div className="mk-td">税率</div>
                <div className="mk-td">税额</div>
              </div>
              <div className="mk-tr">
                <div className="mk-td mk-left">
                  <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                    <div className="mk-popover-control">
                      <SettingComponent config={card.detail} updateConfig={(res) => this.updateComponent({...card, detail: res})} />
                    </div>
                  } trigger="hover">
                    <EllipsisOutlined />
                  </Popover>
                </div>
                <div className="mk-td mk-left"></div>
                <div className="mk-td mk-left"></div>
                <div className="mk-td mk-right"></div>
                <div className="mk-td mk-right"></div>
                <div className="mk-td mk-right"></div>
                <div className="mk-td mk-right"></div>
                <div className="mk-td mk-right"></div>
              </div>
              <div className="mk-total">
                <div className="mk-td">合计</div>
                <div className="mk-td"></div>
                <div className="mk-td"></div>
                <div className="mk-td"></div>
                <div className="mk-td"></div>
                <div className="mk-td">¥12</div>
                <div className="mk-td"></div>
                <div className="mk-td">¥1</div>
              </div>
              <div className="mk-upcase">
                <div className="mk-td">价税合计(大写)</div>
                <div className="mk-td"></div>
                <div className="mk-td">(小写)¥12</div>
              </div>
            </div>
          </div>
          <div className="inv-main-content">
            <div className="inv-buyer">
              <div className="inv-label">销售方</div>
              <div className="inv-content">
                <div className="mk-input">
                  <label>名称:</label>
                  <span className="content">请输入销售方名称</span>
                </div>
                <div className="mk-input">
                  <label>纳税人识别号:</label>
                  <span className="content">请输入销售方纳税人识别号</span>
                </div>
                <div className="mk-input">
                  <label>地址、电话:</label>
                  <span className="content">请输入销售方地址</span>
                  <span className="content">请输入销售方电话</span>
                </div>
                <div className="mk-input">
                  <label>开户行及账号:</label>
                  <span className="content">请输入销售方开户行</span>
                  <span className="content">请输入销售方账号</span>
                </div>
              </div>
            </div>
            <div className="inv-notice">
              <div className="inv-label">备注</div>
              <div className="inv-content" style={{paddingTop: '30px'}}>
                <div className="mk-input">
                  <span className="content" style={{height: '80px'}}>请输入备注</span>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div className="inv-tail">
          <div className="mk-input">
            <label>收款人:</label>
            <span className="content">收款人</span>
          </div>
          <div className="mk-input">
            <label>复核人:</label>
            <span className="content">复核人</span>
          </div>
          <div className="mk-input">
            <label>开票人:</label>
            <span className="content">开票人</span>
          </div>
        </div>
        <Modal
          wrapClassName="mk-pop-modal"
          visible={btn !== null}
          width={'90vw'}
          maskClosable={false}
          okText="提交"
          onOk={this.verifySubmit}
          onCancel={() => {
            if (this.verifyRef.handleCancel) {
              this.verifyRef.handleCancel().then(() => {
                this.setState({ btn: null })
              })
            } else {
              this.setState({ btn: null })
            }
          }}
          destroyOnClose
        >
          <VerifyCard card={btn} wrappedComponentRef={(inst) => this.verifyRef = inst}/>
        </Modal>
      </div>
    )
  }
}
export default Invoice
src/menu/components/module/invoice/index.scss
New file
@@ -0,0 +1,258 @@
.menu-invoice-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  color: #000000;
  --inv-color: #13509c;
  .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);
  }
  .inv-action {
    text-align: right;
    margin-right: 30px;
    .ant-btn {
      margin-left: 15px;
      margin-bottom: 5px;
      height: 30px;
    }
    .mk-bill:hover, .mk-bill:active, .mk-bill:focus {
      color: var(--mk-sys-color);
      border-color: var(--mk-sys-color);
    }
    .mk-submit, .mk-submit:hover, .mk-submit:active, .mk-submit:focus {
      color: #fff;
      background-color: var(--mk-sys-color);
      border-color: var(--mk-sys-color);
    }
  }
  .inv-header {
    text-align: center;
    position: relative;
    height: 70px;
    margin-right: 30px;
    .mk-select {
      position: relative;
      display: inline-block;
      width: 390px;
      font-size: 25px;
      text-align: center;
      font-family: kaiti;
      color: var(--inv-color, #13509c);
    }
    .mk-select::before, .mk-select::after {
      content: '';
      display: block;
      width: 100%;
      position: absolute;
      border-top: var(--inv-color, #13509c) 1px solid;
      border-bottom: var(--inv-color, #13509c) 1px solid;
      height: 1px;
    }
    .mk-select::before {
      bottom: -10px;
    }
    .mk-select::after {
      bottom: -15px;
    }
    .date {
      position: absolute;
      right: 100px;
      top: 5px;
      color: var(--inv-color, #13509c);
    }
  }
  .mk-input {
    position: relative;
    height: 28px;
    font-size: 13px;
    background: transparent;
    display: flex;
    padding: 0 5px 0 15px;
    margin-bottom: 5px;
    label {
      display: inline-block;
      height: 28px;
      line-height: 28px;
      text-align: right;
      color: var(--inv-color, #13509c);
    }
    span {
      display: inline-block;
      height: 28px;
      line-height: 28px;
    }
    .extra {
      position: absolute;
      right: 0px;
      .anticon-ellipsis {
        padding: 0 15px;
      }
    }
    .content {
      flex: 1;
      border-bottom: 1px solid #d9d9d9;
      color: #b8b8b8;
      transition: all 0.3s;
      padding: 0px 10px;
    }
  }
  .mk-input:hover .content {
    border-color: var(--inv-color, #13509c);
  }
  .inv-body {
    border: var(--inv-color, #13509c) 1px solid;
    font-size: 13px;
    margin-right: 30px;
    .inv-main-content {
      display: flex;
      .inv-buyer, .inv-notice {
        width: 50%;
        display: flex;
        .inv-label {
          color: var(--inv-color, #13509c);
          width: 6.25%;
          display: flex;
          flex-direction: column;
          writing-mode: vertical-rl;
          justify-content: center;
          align-items: center;
          letter-spacing: 5px;
          border-right: var(--inv-color, #13509c) 1px solid;
        }
        .inv-content {
          flex: 1;
          padding: 8px 0;
        }
      }
      .inv-buyer {
        border-right: var(--inv-color, #13509c) 1px solid;
        label {
          width: 95px;
          min-width: 95px;
        }
      }
      .inv-notice {
        .inv-content {
          padding-top: 45px;
        }
        label {
          width: 75px;
          min-width: 75px;
        }
      }
    }
    .inv-details {
      border-top: var(--inv-color, #13509c) 1px solid;
      border-bottom: var(--inv-color, #13509c) 1px solid;
    }
  }
  .inv-tail {
    display: flex;
    padding-top: 10px;
    margin-right: 30px;
    .mk-input {
      display: flex;
      flex: 1;
      label {
        width: 40%;
        color: rgba(0, 0, 0, 0.85);
      }
      span {
        width: 40%;
        flex: none;
      }
    }
  }
  .detail-wrap {
    position: relative;
    .mk-th, .mk-tr, .mk-total {
      position: relative;
      display: flex;
      border-bottom: var(--inv-color, #13509c) 1px solid;
      .mk-td {
        width: 10%;
        height: 40px;
        display: flex;
        justify-content: center;
        align-items: center;
        padding: 0 3px;
        position: relative;
      }
      .mk-td:not(:last-child) {
        border-right: var(--inv-color, #13509c) 1px solid;
      }
      .mk-td:first-child {
        width: 20%;
      }
      .mk-td:nth-child(2), .mk-td:nth-child(6) {
        width: 15%;
      }
      .mk-left {
        justify-content: start;
      }
      .mk-right {
        justify-content: end;
      }
      .anticon-ellipsis {
        padding: 0 10px;
        position: absolute;
        right: 0;
      }
    }
    .mk-tr.active, .mk-tr:hover {
      background: var(--mk-sys-color1);
    }
    .mk-upcase {
      display: flex;
      .mk-td {
        width: 40%;
        height: 40px;
        display: flex;
        align-items: center;
        padding: 0 20px;
      }
      .mk-td:first-child {
        width: 20%;
        padding: 0;
        border-right: var(--inv-color, #13509c) 1px solid;
        justify-content: center;
      }
      .mk-td:last-child {
        justify-content: end;
      }
    }
  }
}
.menu-invoice-box::after {
  display: block;
  content: ' ';
  clear: both;
}
.menu-invoice-box:hover {
  z-index: 1;
  box-shadow: 0px 0px 4px #1890ff;
}
src/menu/components/module/invoice/options.jsx
New file
@@ -0,0 +1,81 @@
/**
 * @description Wrap表单配置信息
 */
export default function (wrap) {
  let menu = window.GLOB.customMenu
  let books = []
  let bookids = []
  menu.components.forEach(item => {
    if (item.subtype === 'account') {
      books.push({
        value: item.uuid,
        label: item.name
      })
      bookids.push(item.uuid)
    }
  })
  const wrapForm = [
    {
      type: 'radio',
      field: 'datatype',
      label: '类型',
      initval: wrap.datatype || 'static',
      required: true,
      options: [
        {value: 'static', label: '新增发票'},
        {value: 'dynamic', label: '查看单据'},
      ]
    },
    {
      type: 'text',
      field: 'name',
      label: '组件名称',
      initval: wrap.name || '',
      tooltip: '用于组件间的区分。',
      required: true
    },
    {
      type: 'number',
      field: 'width',
      label: '宽度',
      initval: wrap.width || 24,
      tooltip: '栅格布局,每行等分为24列。',
      min: 1,
      max: 24,
      precision: 0,
      required: true
    },
    {
      type: 'select',
      field: 'supBook',
      label: '账套',
      initval: wrap.supBook || '',
      required: true,
      options: books,
      allowClear: true
    },
    {
      type: 'radio',
      field: 'business_type',
      label: '发票类型',
      initval: wrap.business_type || 'sell',
      required: true,
      options: [
        {value: 'sell', label: '销项发票'},
        {value: 'buy', label: '进项发票'},
      ]
    },
    {
      type: 'color',
      field: 'invColor',
      label: '样式',
      initval: wrap.invColor || '',
      tooltip: '发票组件中边框以及文字的颜色。',
      allowClear: true,
      required: false
    },
  ]
  return wrapForm
}
src/menu/components/module/invoice/verifycard/baseform/index.jsx
New file
@@ -0,0 +1,110 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input } from 'antd'
// import { QuestionCircleOutlined } from '@ant-design/icons'
// import './index.scss'
const { TextArea } = Input
class BaseForm extends Component {
  static propTpyes = {
    verify: PropTypes.object,
    onChange: PropTypes.func
  }
  state = {}
  handleConfirm = () => {
    const { verify } = this.props
    if (verify.type === 'billout') {
      return new Promise((resolve, reject) => {
        this.props.form.validateFieldsAndScroll((err, values) => {
          if (!err) {
            resolve(values)
          }
        })
      })
    } else {
      return Promise.resolve()
    }
  }
  // onOptionChange = (value, key) => {
  //   const { verify } = this.props
  //   let _verify = {...verify, [key]: value}
  //   this.props.onChange(_verify)
  // }
  render() {
    const { getFieldDecorator } = this.props.form
    const { verify } = this.props
    return (
      <Form className="base-form">
        <Row gutter={24}>
          <Col span={8}>
            <Form.Item label="按钮名称">
              <Input value={verify.label} disabled={true}/>
            </Form.Item>
          </Col>
          {/* <Col span={8}>
            <Form.Item label={
              <Tooltip placement="bottomLeft" title="">
                <QuestionCircleOutlined className="mk-form-tip" />
                接口类型
              </Tooltip>
            }>
              <Radio.Group value={verify.intertype} disabled={true}>
                <Radio value="system">系统</Radio>
                <Radio value="custom">自定义</Radio>
              </Radio.Group>
            </Form.Item>
          </Col> */}
          {/* {verify.type === 'billout' ? <Col span={8}>
            <Form.Item label={
              <Tooltip placement="bottomLeft" title="">
                <QuestionCircleOutlined className="mk-form-tip" />
                参数处理
              </Tooltip>
            }>
              {getFieldDecorator('procMode', {
                initialValue: verify.procMode || 'system',
              })(
                <Radio.Group onChange={(e) => {this.onOptionChange(e.target.value, 'procMode')}}>
                  <Radio value="system">系统函数</Radio>
                  <Radio value="none">无</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null} */}
          {verify.type === 'billout' ? <Col span={24}>
            <Form.Item label="测试地址">
              {getFieldDecorator('interface', {
                initialValue: verify.interface || '',
                rules: [
                  { required: true, message: '请输入测试地址!' }
                ]
              })(
                <TextArea rows={2}/>
              )}
            </Form.Item>
          </Col> : null}
          {verify.type === 'billout' ? <Col span={24}>
            <Form.Item label="正式地址">
              {getFieldDecorator('proInterface', {
                initialValue: verify.proInterface || '',
              })(
                <TextArea rows={2}/>
              )}
            </Form.Item>
          </Col> : null}
        </Row>
      </Form>
    )
  }
}
export default Form.create()(BaseForm)
src/menu/components/module/invoice/verifycard/baseform/index.scss
src/menu/components/module/invoice/verifycard/callbackcustomscript/index.jsx
New file
@@ -0,0 +1,294 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Button, Modal, Tooltip, Radio, Select, Switch, notification } from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
import Api from '@/api'
import { checkSQL } from '@/utils/utils-custom.js'
import CodeMirror from '@/templates/zshare/codemirror'
// import './index.scss'
class CustomForm extends Component {
  static propTpyes = {
    systemScripts: PropTypes.array,
    customScripts: PropTypes.array,
    scriptsChange: PropTypes.func
  }
  state = {
    editItem: null,
    loading: false,
    skip: false
  }
  edit = (record) => {
    this.setState({
      editItem: record
    })
    if (this.props.type) {
      this.props.form.setFieldsValue({
        sql: record.sql
      })
    } else {
      this.props.form.setFieldsValue({
        sql: record.sql,
        position: record.position || 'back'
      })
    }
  }
  handleConfirm = () => {
    const { type } = this.props
    const { editItem, skip } = this.state
    // 表单提交时检查输入值是否正确
    this.props.form.validateFieldsAndScroll((err, values) => {
      if (type === 'fullscreen' && err) {
        notification.warning({
          top: 92,
          message: '请输入sql!',
          duration: 5
        })
        return
      }
      if (!err) {
        if (/^[\s\n]+$/.test(values.sql)) {
          notification.warning({
            top: 92,
            message: '请输入sql!',
            duration: 5
          })
          return
        }
        values.uuid = editItem ? editItem.uuid : ''
        values.position = values.position || (editItem ? editItem.position : 'front')
        if (type === 'fullscreen' && editItem) {
          values.status = editItem.status || 'true'
        }
        let pass = checkSQL(values.sql, 'customscript')
        if (!pass) return
        let sql = `
          /* 系统字段 */
          Declare @UserName nvarchar(50), @FullName nvarchar(50), @RoleID nvarchar(512), @mk_departmentcode nvarchar(512), @mk_organization nvarchar(512), @mk_user_type nvarchar(20), @mk_nation nvarchar(50), @mk_province nvarchar(50), @mk_city nvarchar(50), @mk_district nvarchar(50), @mk_address nvarchar(100), @ErrorCode nvarchar(50), @retmsg nvarchar(4000), @account_id nvarchar(50), @account_year_id nvarchar(50), @account_code nvarchar(50), @account_year_code nvarchar(50), @bid nvarchar(50), @tbid nvarchar(50)
          Select @UserName='', @FullName='', @RoleID='', @mk_departmentcode='', @mk_organization='', @mk_user_type='', @mk_nation='', @mk_province='', @mk_city='', @mk_district='', @mk_address='', @ErrorCode='', @retmsg='', @account_id='', @account_year_id='', @account_code='', @account_year_code='', @bid=''
        `
        let _prevCustomScript = '' // 默认sql前执行脚本
        let _backCustomScript = '' // 默认sql后执行脚本
        this.props.customScripts.forEach(item => {
          if (item.status === 'false') return
          if (item.position === 'front') {
            _prevCustomScript += `
            /* 默认sql前脚本 */
            ${values.uuid === item.uuid ? values.sql : item.sql}
            `
          } else {
            _backCustomScript += `
            /* 默认sql后脚本 */
            ${values.uuid === item.uuid ? values.sql : item.sql}
            `
          }
        })
        if (!values.uuid) {
          if (values.position === 'front') {
            _prevCustomScript += `
            /* 默认sql前脚本 */
            ${values.sql}
            `
          } else {
            _backCustomScript += `
            /* 默认sql后脚本 */
            ${values.sql}
            `
          }
        }
        sql += _prevCustomScript + _backCustomScript
        sql += `
          aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg
        `
        // 数据权限
        sql = sql.replace(/@\$|\$@/ig, '')
        sql = sql.replace(/@datam@/ig, `''`)
        sql = sql.replace(/@typename@/ig, `'debug'`)
        if (skip) {
          this.setState({
            skip: false,
            editItem: null
          }, () => {
            this.props.scriptsChange(values)
          })
          this.props.form.setFieldsValue({
            sql: ' '
          })
        } else {
          this.setState({loading: true})
          Api.sDebug(sql).then(res => {
            if (res.status || res.ErrCode === '-2') {
              this.setState({
                loading: false,
                editItem: null
              }, () => {
                this.props.scriptsChange(values)
              })
              this.props.form.setFieldsValue({
                sql: ' '
              })
            } else {
              this.setState({loading: false})
              Modal.error({
                title: res.message
              })
            }
          })
        }
      }
    })
  }
  handleCancel = () => {
    this.setState({
      editItem: null
    })
    this.props.form.setFieldsValue({
      sql: ' '
    })
  }
  selectScript = (value, option) => {
    if (!value || !option) return
    let _sql = this.props.form.getFieldValue('sql')
    if (/^\s+$/.test(_sql)) {
      _sql = ''
    }
    if (_sql) {
      _sql = _sql + `
      `
    }
    _sql = _sql.replace(/\s{6}$/, '')
    _sql = _sql + `/*${option.props.children}*/
    `
    _sql = _sql.replace(/\s{4}$/, '')
    _sql = _sql + value
    this.props.form.setFieldsValue({
      sql: _sql
    })
  }
  render() {
    const { systemScripts, type } = this.props
    const { getFieldDecorator } = this.props.form
    const { editItem, skip } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout} className="verify-form">
        <Row gutter={24}>
          {!type ? <Col span={8}>
            <Form.Item label="报错字段" style={{margin: 0, whiteSpace: 'nowrap'}}>
              errorcode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
            </Form.Item>
          </Col> : null}
          {!type ? <Col span={24} className="sqlfield">
            <Form.Item label="可用字段">
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'公共值,请按照@xxx@格式使用。'}><span style={{color: '#1890ff'}}>BID, ID, LoginUID, SessionUid, UserID, Appkey, time_id, typename, datam</span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'系统变量,系统会定义变量并赋值。'}><span style={{color: '#fa8c16'}}>UserName, FullName, RoleID, mk_departmentcode, mk_organization, mk_user_type, mk_nation, mk_province, mk_city, mk_district, mk_address</span></Tooltip>,&nbsp;
            </Form.Item>
          </Col> : null}
          {!type ? <Col span={8} style={{whiteSpace: 'nowrap'}}>
            <Form.Item style={{marginBottom: 0}} label={
              <Tooltip placement="bottomLeft" title={'自定义脚本与默认sql位置关系。'}>
                <QuestionCircleOutlined className="mk-form-tip" />
                执行位置
              </Tooltip>
            }>
              {getFieldDecorator('position', {
                initialValue: 'front'
              })(
                <Radio.Group>
                  <Radio value="front">sql前</Radio>
                  <Radio value="back">sql后</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null}
          {!type ? <Col span={8}>
            <Form.Item label="快捷添加" style={{marginBottom: 0}}>
              <Select
                showSearch
                dropdownMatchSelectWidth={false}
                filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                onChange={this.selectScript}
              >
                <Select.Option key="default" value="defaultSql">默认sql</Select.Option>
                {systemScripts.map((option, i) =>
                  <Select.Option key={i} value={option.value}>{option.name}</Select.Option>
                )}
              </Select>
            </Form.Item>
          </Col> : null}
          <Col span={5} className="add" style={{whiteSpace: 'nowrap'}}>
            <Button onClick={this.handleConfirm} loading={this.state.loading} className="mk-green" style={{marginBottom: 15, marginLeft: 40}}>
              {type === 'fullscreen' && !editItem ? '添加' : '保存'}
            </Button>
            <Button onClick={this.handleCancel} style={{marginBottom: 15, marginLeft: 10}}>
              取消
            </Button>
          </Col>
          <Col span={3} className="forced" style={{paddingTop: '12px', fontSize: '12px', whiteSpace: 'nowrap'}}>
            强制保存:
            <Switch checked={skip} size="small" onChange={() => this.setState({skip: !skip})}/>
          </Col>
          <Col span={24} className="sql">
            <Form.Item label={
              <Tooltip placement="bottomLeft" title="数据权限替换符 $@ -> /* 或 \'\'、 @$ -> */ 或 \'\'">
                <QuestionCircleOutlined className="mk-form-tip" />
                sql
              </Tooltip>
            }>
              {getFieldDecorator('sql', {
                initialValue: '',
                rules: [
                  {
                    required: true,
                    message: '请输入sql!'
                  }
                ]
              })(<CodeMirror />)}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
  }
}
export default Form.create()(CustomForm)
src/menu/components/module/invoice/verifycard/callbackcustomscript/index.scss
src/menu/components/module/invoice/verifycard/customscript/index.jsx
New file
@@ -0,0 +1,284 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Button, notification, Modal, Tooltip, Select, Switch } from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
import Api from '@/api'
import { checkSQL } from '@/utils/utils-custom.js'
import CodeMirror from '@/templates/zshare/codemirror'
// import './index.scss'
class CustomForm extends Component {
  static propTpyes = {
    type: PropTypes.any,
    systemScripts: PropTypes.array,
    customScripts: PropTypes.array,
    scriptsChange: PropTypes.func
  }
  state = {
    editItem: null,
    loading: false,
    skip: false
  }
  edit = (record) => {
    this.setState({
      editItem: record
    })
    this.props.form.setFieldsValue({
      sql: record.sql
    })
  }
  handleConfirm = () => {
    const { type } = this.props
    const { editItem, skip } = this.state
    // 表单提交时检查输入值是否正确
    this.props.form.validateFieldsAndScroll((err, values) => {
      if (type === 'fullscreen' && err) {
        notification.warning({
          top: 92,
          message: '请输入sql!',
          duration: 5
        })
        return
      }
      if (!err) {
        if (/^[\s\n]+$/.test(values.sql)) {
          notification.warning({
            top: 92,
            message: '请输入sql!',
            duration: 5
          })
          return
        }
        values.uuid = editItem ? editItem.uuid : ''
        if (type === 'fullscreen' && editItem) {
          values.status = editItem.status || 'true'
        }
        let pass = checkSQL(values.sql, 'customscript')
        if (!pass) return
        let sql = `
          /* 系统字段 */
          Declare @UserName nvarchar(50), @FullName nvarchar(50), @RoleID nvarchar(512), @mk_departmentcode nvarchar(512), @mk_organization nvarchar(512), @mk_user_type nvarchar(20), @mk_nation nvarchar(50), @mk_province nvarchar(50), @mk_city nvarchar(50), @mk_district nvarchar(50), @mk_address nvarchar(100), @ErrorCode nvarchar(50), @retmsg nvarchar(4000), @account_id nvarchar(50), @account_year_id nvarchar(50), @account_code nvarchar(50), @account_year_code nvarchar(50), @bid nvarchar(50), @tbid nvarchar(50)
          Select @UserName='', @FullName='', @RoleID='', @mk_departmentcode='', @mk_organization='', @mk_user_type='', @mk_nation='', @mk_province='', @mk_city='', @mk_district='', @mk_address='', @ErrorCode='', @retmsg='', @account_id='', @account_year_id='', @account_code='', @account_year_code='', @bid=''
          /* 发票主表字段 */
          Declare @invoice_type Nvarchar(50), @from_to_name Nvarchar(50), @from_to_tax_no Nvarchar(50), @from_to_addr Nvarchar(100), @from_to_tel Nvarchar(50), @from_to_bank_name Nvarchar(50), @from_to_account_no Nvarchar(50), @from_to_mob Nvarchar(50), @from_to_email Nvarchar(50), @from_to_code Nvarchar(50), @orgname Nvarchar(50), @tax_no Nvarchar(50), @addr Nvarchar(100), @tel Nvarchar(50), @bank_name Nvarchar(50), @account_no Nvarchar(50), @remark Nvarchar(512), @payee Nvarchar(50), @reviewer Nvarchar(50), @drawer Nvarchar(50), @io Nvarchar(50), @orgcode Nvarchar(50), @total_net_amount Decimal(18,2), @total_tax Decimal(18,2), @total_amount Decimal(18,2)
          Select @invoice_type='', @from_to_name='', @from_to_tax_no='', @from_to_addr='', @from_to_tel='', @from_to_bank_name='', @from_to_account_no='', @from_to_mob='', @from_to_email='', @from_to_code='', @orgname='', @tax_no='', @addr='', @tel='', @bank_name='', @account_no='', @remark='', @payee='', @reviewer='', @drawer='', @io='', @orgcode='', @total_net_amount=0, @total_tax=0, @total_amount=0
          /* 发票明细临时表 */
          Declare @details_list table (productcode Nvarchar(50), productname Nvarchar(50), spec Nvarchar(50), unit Nvarchar(50), bill_count Decimal(18,10), unitprice Decimal(18,10), amount_line Decimal(18,2), tax_classify_code Nvarchar(50), tax_classify_name Nvarchar(50), tax_rate Decimal(18,2), tax_amount Decimal(18,2), free_tax_mark Nvarchar(50), vat_special_management Nvarchar(50), invoice_lp Nvarchar(50), jskey Nvarchar(50), data_type Nvarchar(50))
          Insert into @details_list (productcode, productname, spec, unit, bill_count, unitprice, amount_line, tax_classify_code, tax_classify_name, tax_rate, tax_amount, free_tax_mark, vat_special_management, invoice_lp, jskey, data_type)
          Select '', '', '', '', 0, 0, 0, '', '', 0, 0, '', '', '', '', ''
        `
        this.props.customScripts.forEach(item => {
          let _item = values.uuid === item.uuid ? values : item
          if (_item.status === 'false') return
          sql += `
          ${_item.sql}
          `
        })
        if (!values.uuid) {
          sql += `
          ${values.sql}
          `
        }
        sql += `
          aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg
        `
        // 数据权限
        sql = sql.replace(/@\$|\$@/ig, '')
        sql = sql.replace(/@datam@/ig, `''`)
        sql = sql.replace(/@typename@/ig, `'debug'`)
        if (skip) {
          this.setState({
            skip: false,
            editItem: null
          }, () => {
            this.props.scriptsChange(values)
          })
          this.props.form.setFieldsValue({
            sql: ' '
          })
        } else {
          this.setState({loading: true})
          Api.sDebug(sql).then(res => {
            if (res.status || res.ErrCode === '-2') {
              this.setState({
                loading: false,
                editItem: null
              }, () => {
                this.props.scriptsChange(values)
              })
              this.props.form.setFieldsValue({
                sql: ' '
              })
            } else {
              this.setState({loading: false})
              Modal.error({
                title: res.message
              })
            }
          })
        }
      }
    })
  }
  handleCancel = () => {
    this.setState({
      editItem: null
    })
    this.props.form.setFieldsValue({
      sql: ' '
    })
  }
  selectScript = (value, option) => {
    if (!value || !option) return
    let _sql = this.props.form.getFieldValue('sql')
    if (/^\s+$/.test(_sql)) {
      _sql = ''
    }
    if (_sql) {
      _sql = _sql + `
      `
    }
    _sql = _sql.replace(/\s{6}$/, '')
    _sql = _sql + `/*${option.props.children}*/
    `
    _sql = _sql.replace(/\s{4}$/, '')
    _sql = _sql + value
    this.props.form.setFieldsValue({
      sql: _sql
    })
  }
  render() {
    const { systemScripts, type } = this.props
    const { getFieldDecorator } = this.props.form
    const { editItem, skip } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout} className="verify-form">
        <Row gutter={24}>
          {!type ? <Col span={8}>
            <Form.Item label={'报错字段'} style={{margin: 0, whiteSpace: 'nowrap'}}>
              errorcode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
            </Form.Item>
          </Col> : null}
          {!type ? <Col span={24} className="sqlfield">
            <Form.Item label={'可用字段'}>
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="公共值,请按照@xxx@格式使用。"><span style={{color: '#1890ff'}}>BID, ID, LoginUID, SessionUid, UserID, Appkey, time_id, typename, datam</span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="系统变量,系统会定义变量并赋值。"><span style={{color: '#fa8c16'}}>UserName, FullName, RoleID, mk_departmentcode, mk_organization, mk_user_type, mk_nation, mk_province, mk_city, mk_district, mk_address</span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="账套字段,系统会定义变量并赋值。"><span style={{color: '#13c2c2'}}>account_id, account_year_id, account_code, account_year_code </span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="主表字段,系统会定义变量并赋值。"><span style={{color: '#8E44AD'}}>invoice_type, from_to_name, from_to_tax_no, from_to_addr, from_to_tel, from_to_bank_name, from_to_account_no, from_to_mob, from_to_email, from_to_code, orgname, tax_no, addr, tel, bank_name, account_no, remark, payee, reviewer, drawer</span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="子表字段(商品明细),系统会定义变量并赋值。">productcode, productname, spec, unit, bill_count, unitprice, amount_line, tax_classify_code, tax_classify_name, tax_rate, tax_amount</Tooltip>
            </Form.Item>
          </Col> : null}
          {/* {!_type ? <Col span={8} style={{whiteSpace: 'nowrap'}}>
            <Form.Item style={{marginBottom: 0}} label={
              <Tooltip placement="bottomLeft" title="自定义脚本与默认sql位置关系。">
                <QuestionCircleOutlined className="mk-form-tip" />
                执行位置
              </Tooltip>
            }>
              {getFieldDecorator('position', {
                initialValue: 'front'
              })(
                <Radio.Group>
                  <Radio value="init">初始化</Radio>
                  <Radio value="front">sql前</Radio>
                  <Radio value="back">sql后</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null} */}
          {!type ? <Col span={8}>
            <Form.Item label={'快捷添加'} style={{marginBottom: 0}}>
              <Select
                showSearch
                dropdownMatchSelectWidth={false}
                filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                onSelect={this.selectScript}
              >
                {systemScripts.map((option, i) =>
                  <Select.Option key={i} value={option.value}>{option.name}</Select.Option>
                )}
              </Select>
            </Form.Item>
          </Col> : null}
          <Col span={5} className="add" style={{whiteSpace: 'nowrap'}}>
            <Button onClick={this.handleConfirm} loading={this.state.loading} className="mk-green" style={{marginBottom: 15, marginLeft: 30}}>
              {type === 'fullscreen' && !editItem ? '添加' : '保存'}
            </Button>
            <Button onClick={this.handleCancel} style={{marginBottom: 15, marginLeft: 10}}>
              取消
            </Button>
          </Col>
          <Col span={3} className="forced" style={{paddingTop: '12px', fontSize: '12px', whiteSpace: 'nowrap'}}>
            强制保存:
            <Switch checked={skip} size="small" onChange={() => this.setState({skip: !skip})}/>
          </Col>
          <Col span={24} className="sql">
            <Form.Item label={
              <Tooltip placement="bottomLeft" title="数据权限替换符 $@ -> /* 或 \'\'、 @$ -> */ 或 \'\'">
                <QuestionCircleOutlined className="mk-form-tip" />
                sql
              </Tooltip>
            }>
              {getFieldDecorator('sql', {
                initialValue: '',
                rules: [
                  {
                    required: true,
                    message: '请输入sql!'
                  }
                ]
              })(<CodeMirror />)}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
  }
}
export default Form.create()(CustomForm)
src/menu/components/module/invoice/verifycard/customscript/index.scss
src/menu/components/module/invoice/verifycard/index.jsx
New file
@@ -0,0 +1,690 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Form, Tabs, Row, Col, Button, Popconfirm, notification, Modal, message, Typography, InputNumber } from 'antd'
import { CheckCircleOutlined, StopOutlined, EditOutlined, SwapOutlined, DeleteOutlined } from '@ant-design/icons'
import moment from 'moment'
import Api from '@/api'
import Utils from '@/utils/utils.js'
import BaseForm from './baseform'
import CustomScript from './customscript'
import CallBackCustomScript from './callbackcustomscript'
import asyncComponent from '@/utils/asyncComponent'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const { TabPane } = Tabs
const { confirm } = Modal
const { Paragraph } = Typography
const EditTable = asyncComponent(() => import('@/templates/zshare/editTable'))
const FullScripts = asyncComponent(() => import('@/templates/zshare/verifycard/fullScripts'))
class VerifyCard extends Component {
  static propTpyes = {
    config: PropTypes.any,
    card: PropTypes.object,
    columns: PropTypes.array
  }
  state = {
    activeKey: 'base',
    verify: {},
    systemScripts: [],
    scriptsColumns: [
      {
        title: 'SQL',
        dataIndex: 'sql',
        width: '70%',
        render: (text) => {
          let title = text.match(/^\s*\/\*.+\*\//)
          title = title && title[0] ? title[0] : ''
          let _text = title ? text.replace(title, '') : text
          return (
            <div>
              {title ? <span style={{color: '#a50'}}>{title}<span style={{fontSize: '12px', marginLeft: '5px'}}>{_text.length}</span></span> : null}
              <Paragraph copyable={{ text: text }} ellipsis={{ rows: 4, expandable: true }}>{_text}</Paragraph>
            </div>
          )
        }
      },
      // {
      //   title: '执行位置',
      //   dataIndex: 'position',
      //   width: '10%',
      //   render: (text, record) => {
      //     if (record.position === 'init') {
      //       return <span style={{color: 'orange'}}>初始化</span>
      //     } else if (record.position === 'front') {
      //       return <span style={{color: '#26C281'}}>sql前</span>
      //     } else {
      //       return <span style={{color: '#1890ff'}}>sql后</span>
      //     }
      //   }
      // },
      {
        title: '状态',
        dataIndex: 'status',
        width: '10%',
        render: (text, record) => record.status === 'false' ?
          (
            <div style={{color: '#ff4d4f'}}>
              禁用
              <StopOutlined style={{marginLeft: '5px'}} />
            </div>
          ) :
          (
            <div style={{color: '#26C281'}}>
              启用
              <CheckCircleOutlined style={{marginLeft: '5px'}}/>
            </div>
          )
      },
      {
        title: '操作',
        align: 'center',
        width: '140px',
        dataIndex: 'operation',
        render: (_, record) =>
          (<div style={{textAlign: 'center'}}>
            <span className="operation-btn" title="编辑" onClick={() => this.handleEdit(record, 'scripts')} style={{color: '#1890ff'}}><EditOutlined /></span>
            <span className="operation-btn" title="状态切换" onClick={() => this.handleStatus(record, 'scripts')} style={{color: '#8E44AD'}}><SwapOutlined /></span>
            <Popconfirm
              overlayClassName="popover-confirm"
              title="确定删除吗?"
              onConfirm={() => this.handleDelete(record, 'scripts')
            }>
              <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
            </Popconfirm>
          </div>)
      }
    ],
    cbScriptsColumns: [
      {
        title: 'SQL',
        dataIndex: 'sql',
        width: '60%',
        render: (text) => {
          let title = text.match(/^\s*\/\*.+\*\//)
          title = title && title[0] ? title[0] : ''
          let _text = title ? text.replace(title, '') : text
          return (
            <div>
              {title ? <span style={{color: '#a50'}}>{title}<span style={{fontSize: '12px', marginLeft: '5px'}}>{_text.length}</span></span> : null}
              <Paragraph copyable={{ text: text }} ellipsis={{ rows: 4, expandable: true }}>{_text}</Paragraph>
            </div>
          )
        }
      },
      {
        title: '执行位置',
        dataIndex: 'position',
        width: '10%',
        render: (_, record) => {
          if (record.position === 'front') {
            return <span style={{color: '#26C281'}}>sql前</span>
          } else {
            return <span style={{color: '#1890ff'}}>sql后</span>
          }
        }
      },
      {
        title: '状态',
        dataIndex: 'status',
        width: '10%',
        render: (_, record) => record.status === 'false' ?
          (
            <div style={{color: '#ff4d4f'}}>
              禁用
              <StopOutlined style={{marginLeft: '5px'}} />
            </div>
          ) :
          (
            <div style={{color: '#26C281'}}>
              启用
              <CheckCircleOutlined style={{marginLeft: '5px'}}/>
            </div>
          )
      },
      {
        title: '操作',
        align: 'center',
        width: '20%',
        dataIndex: 'operation',
        render: (text, record) =>
          (<div style={{textAlign: 'center'}}>
            <span className="operation-btn" title="编辑" onClick={() => this.handleEdit(record, 'cbscripts')} style={{color: '#1890ff'}}><EditOutlined /></span>
            <span className="operation-btn" title="状态切换" onClick={() => this.handleStatus(record, 'cbscripts')} style={{color: '#8E44AD'}}><SwapOutlined /></span>
            <Popconfirm
              overlayClassName="popover-confirm"
              title="确定删除吗?"
              onConfirm={() => this.handleDelete(record, 'cbscripts')
            }>
              <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
            </Popconfirm>
          </div>)
      }
    ]
  }
  UNSAFE_componentWillMount() {
    const { card } = this.props
    let _verify = fromJS(card).toJS()
    // _verify.intertype = _verify.intertype || 'system'
    _verify.scripts = _verify.scripts || []
    _verify.cbScripts = _verify.cbScripts || []
    _verify.scripts.forEach((item, i) => {
      item.$index = i + 1
    })
    _verify.cbScripts.forEach((item, i) => {
      item.$index = i + 1
    })
    this.setState({
      activeKey: 'base',
      verify: _verify,
      oriVerify: fromJS(_verify).toJS()
    })
  }
  componentDidMount() {
    let mutilForms = [
      {
        obj_name: 'scripts',
        arr_field: 'funcname,longparam',
        LText: window.btoa(window.encodeURIComponent(`Select distinct func+Remark as funcname,longparam, s.Sort from s_custom_script s inner join (select OpenID from sapp where ID=@Appkey@) p on s.openid = case when s.appkey='' then s.openid else p.OpenID end order by s.Sort`))
      }
    ]
    mutilForms = mutilForms.map(item => `select '${item.obj_name}' as obj_name,'${item.arr_field}' as arr_field,'${item.LText}' as LText`)
    let mutilparam = {
      func: 'sPC_Get_SelectedList',
      LText: mutilForms.join(' union all '),
      obj_name: '',
      arr_field: '',
      table_type: 'Y',
      exec_type: 'x'
    }
    mutilparam.LText = Utils.formatOptions(mutilparam.LText, 'x')
    mutilparam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
    mutilparam.secretkey = Utils.encrypt('', mutilparam.timestamp)
    mutilparam.open_key = Utils.encryptOpenKey(mutilparam.secretkey, mutilparam.timestamp)
    if (window.GLOB.cloudServiceApi) { // 云端请求
      mutilparam.rduri = window.GLOB.cloudServiceApi
      mutilparam.userid = sessionStorage.getItem('CloudUserID') || ''
      mutilparam.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
    }
    Api.getSystemCacheConfig(mutilparam).then(res => {
      if (res.status) {
        this.setState({
          systemScripts: res.scripts.map(item => {
            return {
              name: item.funcname,
              value: window.decodeURIComponent(window.atob(item.longparam))
            }
          })
        })
      } else {
        notification.warning({
          top: 92,
          message: res.message,
          duration: 5
        })
      }
    })
  }
  scriptsChange = (values) => {
    let verify = fromJS(this.state.verify).toJS()
    if (values.uuid) {
      verify.scripts = verify.scripts.map(item => {
        if (item.uuid === values.uuid) {
          return values
        } else {
          return item
        }
      })
    } else {
      values.uuid = Utils.getuuid()
      verify.scripts.push(values)
    }
    MKEmitter.emit('editLineId', values.uuid)
    this.setState({ verify })
  }
  cbScriptsChange = (values) => {
    let verify = fromJS(this.state.verify).toJS()
    if (values.uuid) {
      verify.cbScripts = verify.cbScripts.map(item => {
        if (item.uuid === values.uuid) {
          return values
        } else {
          return item
        }
      })
    } else {
      values.uuid = Utils.getuuid()
      verify.cbScripts.push(values)
    }
    MKEmitter.emit('editLineId', values.uuid)
    this.setState({ verify })
  }
  handleDelete = (record, type) => {
    const { verify } = this.state
    if (type === 'scripts') {
      verify.scripts = verify.scripts.filter(item => item.uuid !== record.uuid)
    } else if (type === 'cbscripts') {
      verify.cbScripts = verify.cbScripts.filter(item => item.uuid !== record.uuid)
    }
    this.setState({ verify })
  }
  handleEdit = (record, type) => {
    let node = null
    if (type === 'scripts') {
      this.scriptsForm.edit(record)
      node = document.getElementById('mk-normal-script')
    } else if (type === 'cbscripts') {
      this.cbscriptsForm.edit(record)
      node = document.getElementById('mk-callback-script')
    }
    if (node && node.scrollTop) {
      let inter = Math.ceil(node.scrollTop / 10)
      let timer = setInterval(() => {
        if (node.scrollTop - inter > 0) {
          node.scrollTop = node.scrollTop - inter
        } else {
          node.scrollTop = 0
          clearInterval(timer)
        }
      }, 10)
    }
  }
  handleStatus = (record, type) => {
    let verify = fromJS(this.state.verify).toJS()
    record.status = record.status === 'false' ? 'true' : 'false'
    if (type === 'scripts') {
      verify.scripts = verify.scripts.map(item => {
        if (item.uuid === record.uuid) {
          return record
        } else {
          return item
        }
      })
    } else if (type === 'cbscripts') {
      verify.cbScripts = verify.cbScripts.map(item => {
        if (item.uuid === record.uuid) {
          return record
        } else {
          return item
        }
      })
    }
    this.setState({ verify })
  }
  showError = (errorType) => {
    const { verify } = this.state
    if (errorType === 'S') {
      let time = verify.stime || 2
      notification.success({
        top: 92,
        message: '执行成功!',
        duration: time
      })
    } else if (errorType === 'Y') {
      Modal.success({
        title: '执行成功!'
      })
    } else if (errorType === 'F') {
      notification.error({
        className: 'notification-custom-error',
        top: 92,
        message: '执行失败!',
        duration: verify.ftime || 10
      })
    } else if (errorType === 'N') {
      notification.error({
        top: 92,
        message: '执行失败!',
        duration: verify.ntime || 10
      })
    } else if (errorType === 'E') {
      Modal.error({
        title: '执行失败!'
      })
    } else if (errorType === 'NM') {
      message.error('执行失败!')
    }
  }
  timeChange = (val, type) => {
    const { verify } = this.state
    this.setState({
      verify: {...verify, [type]: val}
    })
  }
  handleConfirm = () => {
    const { activeKey } = this.state
    let verify = fromJS(this.state.verify).toJS()
    let msg = ''
    if (this.scriptsForm && this.scriptsForm.state.editItem) {
      msg = '前置脚本'
    } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
      msg = '前置脚本'
    } else if (this.cbscriptsForm && this.cbscriptsForm.state.editItem) {
      msg = '回调脚本'
    } else if (this.cbscriptsForm && this.cbscriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.cbscriptsForm.props.form.getFieldValue('sql'))) {
      msg = '回调脚本'
    }
    return new Promise((resolve, reject) => {
      if (activeKey === 'base' && verify.type === 'billout') {
        this.baseForm.handleConfirm().then(res => {
          let _verify = {...verify, ...res}
          if (msg) {
            confirm({
              content: msg + '未保存,确定提交吗?',
              onOk() {
                resolve(_verify)
              },
              onCancel() {}
            })
          } else {
            resolve(_verify)
          }
        })
      } else {
        if (msg) {
          confirm({
            content: msg + '未保存,确定提交吗?',
            onOk() {
              resolve(verify)
            },
            onCancel() {}
          })
        } else {
          resolve(verify)
        }
      }
    })
  }
  handleCancel = () => {
    const { verify, oriVerify } = this.state
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      if (!is(fromJS(verify), fromJS(oriVerify))) {
        confirm({
          content: '按钮信息已修改,确定取消吗?',
          onOk() {
            resolve()
          },
          onCancel() {}
        })
      } else {
        resolve()
      }
    })
  }
  changeTab = (val) => {
    const { activeKey, verify } = this.state
    if (activeKey === 'base') {
      this.baseForm.handleConfirm().then(res => {
        this.setState({
          verify: {...verify, ...res},
          activeKey: val
        })
      })
    } else {
      this.setState({
        activeKey: val
      })
    }
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  render() {
    const { card, columns } = this.props
    const { activeKey, verify, scriptsColumns, cbScriptsColumns } = this.state
    return (
      <div>
        <div className="mk-com-name">{card.label}</div>
        <Tabs activeKey={activeKey} className="mk-invoice-tabs" onChange={this.changeTab}>
          <TabPane tab="基础设置" key="base">
            <BaseForm columns={columns} verify={verify} onChange={(verify) => this.setState({verify})} wrappedComponentRef={(inst) => this.baseForm = inst}/>
          </TabPane>
          <TabPane tab={
            <span>
              前置脚本
              {verify.scripts.length ? <span className="count-tip">{verify.scripts.length}</span> : null}
            </span>
          } key="scripts" id="mk-normal-script">
            <FullScripts
              scripts={verify.scripts}
              getScriptsFullForm={() => this.scriptsFullForm}
              getScriptsForm={() => this.scriptsForm}
              handleStatus={this.handleStatus}
              handleDelete={this.handleDelete}
            >
              <CustomScript
                type="fullscreen"
                customScripts={verify.scripts}
                systemScripts={this.state.systemScripts}
                scriptsChange={this.scriptsChange}
                wrappedComponentRef={(inst) => this.scriptsFullForm = inst}
              />
            </FullScripts>
            <CustomScript
              customScripts={verify.scripts}
              systemScripts={this.state.systemScripts}
              scriptsChange={this.scriptsChange}
              wrappedComponentRef={(inst) => this.scriptsForm = inst}
            />
            <EditTable actions={['move']} data={verify.scripts} columns={scriptsColumns} onChange={(scripts) => {this.setState({verify: {...verify, scripts}})}}/>
          </TabPane>
          {card.type === 'billout' ? <TabPane tab={
            <span>
              回调脚本
              {verify.cbScripts.length ? <span className="count-tip">{verify.cbScripts.length}</span> : null}
            </span>
          } key="cbScripts" id="mk-callback-script">
            <FullScripts
              scripts={verify.cbScripts}
              getScriptsFullForm={() => this.cbscriptsFullForm}
              getScriptsForm={() => this.cbscriptsForm}
              handleStatus={(item) => this.handleStatus(item, 'cbscripts')}
              handleDelete={(item) => this.handleDelete(item, 'cbscripts')}
            >
              <CallBackCustomScript
                type="fullscreen"
                customScripts={verify.cbScripts}
                systemScripts={this.state.systemScripts}
                scriptsChange={this.cbScriptsChange}
                wrappedComponentRef={(inst) => this.cbscriptsFullForm = inst}
              />
            </FullScripts>
            <CallBackCustomScript
              customScripts={verify.cbScripts}
              systemScripts={this.state.systemScripts}
              scriptsChange={this.cbScriptsChange}
              wrappedComponentRef={(inst) => this.cbscriptsForm = inst}
            />
            <EditTable actions={['move']} data={verify.cbScripts} columns={cbScriptsColumns} onChange={(cbScripts) => {this.setState({verify: {...verify, cbScripts}})}}/>
          </TabPane> : null}
          <TabPane tab="sql示例" key="sql">
            <div className="mk-sql-wrap">
              <p className="note">{`/* 系统字段 */`}</p>
              <p>
                Declare @UserName nvarchar(50), @FullName nvarchar(50), @RoleID nvarchar(512), @mk_departmentcode nvarchar(512), @mk_organization nvarchar(512), @mk_user_type nvarchar(20), @mk_nation nvarchar(50), @mk_province nvarchar(50), @mk_city nvarchar(50), @mk_district nvarchar(50), @mk_address nvarchar(100), @ErrorCode nvarchar(50), @retmsg nvarchar(4000), @account_id nvarchar(50), @account_year_id nvarchar(50), @account_code nvarchar(50), @account_year_code nvarchar(50), @bid nvarchar(50), @tbid nvarchar(50)
              </p>
              <p>
                Select @UserName='', @FullName='', @RoleID='', @mk_departmentcode='', @mk_organization='', @mk_user_type='', @mk_nation='', @mk_province='', @mk_city='', @mk_district='', @mk_address='', @ErrorCode='', @retmsg='', @account_id='', @account_year_id='', @account_code='', @account_year_code='', @bid=''
              </p>
              <p className="note">{`/* 发票主表字段 */`}</p>
              <p>
                Declare @invoice_type Nvarchar(50), @from_to_name Nvarchar(50), @from_to_tax_no Nvarchar(50), @from_to_addr Nvarchar(100), @from_to_tel Nvarchar(50), @from_to_bank_name Nvarchar(50), @from_to_account_no Nvarchar(50), @from_to_mob Nvarchar(50), @from_to_email Nvarchar(50), @from_to_code Nvarchar(50), @orgname Nvarchar(50), @tax_no Nvarchar(50), @addr Nvarchar(100), @tel Nvarchar(50), @bank_name Nvarchar(50), @account_no Nvarchar(50), @remark Nvarchar(512), @payee Nvarchar(50), @reviewer Nvarchar(50), @drawer Nvarchar(50), @io Nvarchar(50), @orgcode Nvarchar(50), @total_net_amount Decimal(18,2), @total_tax Decimal(18,2), @total_amount Decimal(18,2)
              </p>
              <p>
                Select @invoice_type='', @from_to_name='', @from_to_tax_no='', @from_to_addr='', @from_to_tel='', @from_to_bank_name='', @from_to_account_no='', @from_to_mob='', @from_to_email='', @from_to_code='', @orgname='', @tax_no='', @addr='', @tel='', @bank_name='', @account_no='', @remark='', @payee='', @reviewer='', @drawer='', @io='', @orgcode='', @total_net_amount=0, @total_tax=0, @total_amount=0
              </p>
              <p className="note">{`/* 发票明细临时表 */`}</p>
              <p>
                Declare @details_list table (productcode Nvarchar(50), productname Nvarchar(50), spec Nvarchar(50), unit Nvarchar(50), bill_count Decimal(18,10), unitprice Decimal(18,10), amount_line Decimal(18,2), tax_classify_code Nvarchar(50), tax_classify_name Nvarchar(50), tax_rate Decimal(18,2), tax_amount Decimal(18,2), free_tax_mark Nvarchar(50), vat_special_management Nvarchar(50), invoice_lp Nvarchar(50), jskey Nvarchar(50), data_type Nvarchar(50))
              </p>
              <p>
                Insert into @details_list (productcode, productname, spec, unit, bill_count, unitprice, amount_line, tax_classify_code, tax_classify_name, tax_rate, tax_amount, free_tax_mark, vat_special_management, invoice_lp, jskey, data_type)
              </p>
              <p>
                Select '', '', '', '', 0, 0, 0, '', '', 0, 0, '', '', '', '', ''
              </p>
              <p className="note">{`/* 前置脚本 */`}</p>
              <p>
                ......
              </p>
              <p>
                aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg
              </p>
            </div>
          </TabPane>
          <TabPane tab="信息提示" key="tip">
            <Form className="tip-form">
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> S </span>
                    <Button onClick={() => {this.showError('S')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
                <Col span={8}>
                  <Form.Item label="停留时间">
                    <InputNumber defaultValue={verify.stime || 2} min={1} max={10000} precision={0} onChange={(val) => {this.timeChange(val, 'stime')}} />
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> Y </span>
                    <Button onClick={() => {this.showError('Y')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> -1 </span>
                    执行成功无提示。
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> N </span>
                    <Button onClick={() => {this.showError('N')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
                <Col span={8}>
                  <Form.Item label="停留时间">
                    <InputNumber defaultValue={verify.ntime || 10} min={1} max={10000} precision={0} onChange={(val) => {this.timeChange(val, 'ntime')}} />
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> F </span>
                    <Button onClick={() => {this.showError('F')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
                <Col span={8}>
                  <Form.Item label="停留时间">
                    <InputNumber defaultValue={verify.ftime || 10} min={1} max={10000} precision={0} onChange={(val) => {this.timeChange(val, 'ftime')}} />
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> E </span>
                    <Button onClick={() => {this.showError('E')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> NM </span>
                    <Button onClick={() => {this.showError('NM')}} type="primary" size="small">
                      查看
                    </Button>
                  </Form.Item>
                </Col>
              </Row>
              <Row gutter={24}>
                <Col offset={6} span={6}>
                  <Form.Item label="提示编码">
                    <span className="errorval"> -2 </span>
                    执行失败无提示
                  </Form.Item>
                </Col>
              </Row>
            </Form>
          </TabPane>
        </Tabs>
      </div>
    )
  }
}
export default Form.create()(VerifyCard)
src/menu/components/module/invoice/verifycard/index.scss
New file
@@ -0,0 +1,124 @@
.mk-invoice-tabs {
  .ant-tabs-nav .ant-tabs-tab {
    margin-right: 25px;
  }
  .ant-tabs-nav-scroll {
    text-align: center;
  }
  .ant-tabs-content {
    min-height: 40vh;
  }
  table tr td {
    word-wrap: break-word;
    word-break: break-word;
  }
  .count-tip {
    position: absolute;
    top: 0px;
    color: #1890ff;
    font-size: 12px;
  }
  .ant-input-disabled {
    color: rgba(0, 0, 0, 0.85);
    background-color: #ffffff;
    cursor: text;
  }
  .ant-radio-disabled + span {
    color: rgba(0, 0, 0, 0.85);
  }
  .base-form {
    padding-right: 50px;
    .ant-form-item {
      display: flex;
      .ant-form-item-control-wrapper {
        flex: 1;
      }
    }
    .ant-col-8 {
      .ant-form-item-label {
        width: 33.33%;
      }
    }
    .ant-col-24 {
      .ant-form-item-label {
        width: 10.8%;
      }
    }
  }
  .tip-form {
    .ant-form-item {
      display: flex;
    }
  }
  .mk-sql-wrap {
    color: rgba(0, 0, 0, 0.85);
    .note {
      margin-bottom: 0px;
    }
  }
  .verify-form {
    .sql {
      .ant-col-sm-8 {
        width: 10.5%;
      }
      .ant-col-sm-16 {
        width: 89.5%;
        padding-top: 4px;
      }
      .CodeMirror {
        height: 350px;
      }
    }
    .sqlfield {
      .ant-form-item {
        margin-bottom: 5px;
      }
      .ant-form-item-control {
        line-height: 24px;
      }
      .ant-form-item-label {
        line-height: 25px;
      }
      .ant-form-item-children {
        line-height: 22px;
      }
      .ant-col-sm-8 {
        width: 10.5%;
      }
      .ant-col-sm-16 {
        width: 89.5%;
      }
    }
    .add {
      padding-top: 4px;
      .mk-green {
        margin-bottom: 15px;
      }
    }
  }
  .custom-table .ant-empty {
    margin: 20px 8px!important;
  }
  .errorval {
    display: inline-block;
    width: 30px;
  }
  .operation-btn {
    display: inline-block;
    font-size: 16px;
    padding: 0 5px;
    cursor: pointer;
  }
  .operation-btn:not(:first-child) {
    margin-left: 5px;
  }
  .full-scripts {
    position: absolute;
    right: 24px;
    top: 0px;
    font-size: 16px;
    color: #1890ff;
    z-index: 1;
  }
}
src/menu/components/module/voucher/index.jsx
@@ -122,16 +122,17 @@
          <ToolOutlined />
        </Popover>
        <div className="voucher-box">
          {card.wrap.type === 'createVoucher' ? <div className="voucher-header">
            <Button className="add-background header-btn">保存并新增</Button>
            <Button className="add-background header-btn">保存</Button>
            <Button className="print-background header-btn">打印</Button>
            <Button className="out-background header-btn">更多</Button>
          {card.wrap.type === 'createVoucher' ? <div className="voucher-header" style={{padding: `10px ${card.wrap.space || 0}px`}}>
            <Button>保存并新增</Button>
            <Button>保存</Button>
            <Button>打印</Button>
            <Button>更多</Button>
          </div> : null}
          {card.wrap.type === 'checkVoucher' ? <div className="voucher-header">
            <Button className="add-background header-btn">保存</Button>
            <Button className="print-background header-btn">打印</Button>
            <Button className="out-background header-btn">关闭</Button>
          {card.wrap.type === 'checkVoucher' ? <div className="voucher-header" style={{padding: `10px ${card.wrap.space || 0}px`}}>
            <Button>保存</Button>
            <Button>打印</Button>
            <Button>审核</Button>
            <Button>关闭</Button>
          </div> : null}
          <div className="voucher-body" style={{padding: `0px ${card.wrap.space || 0}px`}}>
            {card.wrap.type === 'createVoucher' || card.wrap.type === 'checkVoucher' ? <div className="pre-wrap">
@@ -158,8 +159,8 @@
                模板类型:
                <div>日常支出<DownOutlined/></div>
              </div>
              <Button className="close-temp header-btn">关闭</Button>
              <Button className="save-temp header-btn">保存</Button>
              <Button>关闭</Button>
              <Button>保存</Button>
            </div> : null}
            <VoucherTable config={card}/>
            {card.wrap.type === 'createVoucher' || card.wrap.type === 'checkVoucher' ? <div className="user">制单人:</div> : null}
src/menu/components/module/voucher/index.scss
@@ -24,10 +24,13 @@
    padding: 10px;
    border-bottom: 1px solid #eeeeee;
    .header-btn {
    .ant-btn {
      height: 28px;
      min-width: 80px;
      margin-right: 10px;
      background-color: #ffffff;
      border-color: #d8d8d8;
      color: rgba(0, 0, 0, 0.65);
    }
  }
  .voucher-body {
@@ -76,12 +79,14 @@
        }
      }
    }
    .header-btn {
      float: right;
      margin-left: 10px;
    }
    .pre-wrap {
      padding: 10px 0px;
      >.ant-btn {
        margin-right: 10px;
        background-color: #ffffff;
        border-color: #d8d8d8;
        color: rgba(0, 0, 0, 0.65);
      }
    }
    .voucher-date {
      display: inline-block;
@@ -117,38 +122,6 @@
    .user {
      padding-top: 15px;
    }
  }
  .add-background {
    background: #26C281;
    border-color: #26C281;
    color: #ffffff;
  }
  .print-background {
    background-color: #8E44AD;
    border-color: #8E44AD;
    color: #ffffff;
  }
  .out-background {
    background-color: rgb(50, 197, 210);
    border-color: rgb(50, 197, 210);
    color: #ffffff;
  }
  .save-temp {
    background-color: #1890ff;
    border-color: #1890ff;
    color: #ffffff;
  }
  .close-temp {
    background-color: #ffffff;
    border-color: #f5222d;
    color: #f5222d;
  }
  .system-background {
    background: #1890ff;
    border-color: #1890ff;
    color: #ffffff;
  }
}
.menu-voucher-box::after {
src/menu/components/module/voucher/options.jsx
@@ -1,3 +1,4 @@
import React from 'react'
import MenuUtils from '@/utils/utils-custom.js'
/**
@@ -56,6 +57,7 @@
        {field: 'voucherType', values: ['createVoucher', 'checkVoucher']},
        {field: 'voucherTypeText', values: ['createVoucher', 'checkVoucher']},
        {field: 'voucherSign', values: ['createVoucher', 'checkVoucher']},
        {field: 'printTemp', values: ['createVoucher', 'checkVoucher']},
        {field: 'supModule', values: ['checkTemp', 'checkVoucher']},
        {field: 'attachStatus', values: ['createVoucher', 'checkVoucher']},
      ]
@@ -138,6 +140,26 @@
      ]
    },
    {
      type: 'printTemps',
      field: 'printTemp',
      label: '打印模板',
      initval: wrap.printTemp || '',
      help: (record) => {
        if (record.printTemp) {
          return <span onClick={() => {
            sessionStorage.setItem('mk-print-temp', record.printTemp)
            window.open('#/hs')
            setTimeout(() => {
              sessionStorage.removeItem('mk-print-temp')
            }, 50)
          }} style={{color: '#1890ff', cursor: 'pointer', fontSize: '13px'}}>#查看模板</span>
        }
        return ''
      },
      required: true
    },
    {
      type: 'number',
      field: 'space',
      label: '留白',
src/menu/components/module/voucher/voucherTable/index.scss
@@ -10,9 +10,8 @@
    position: absolute;
    bottom: 10px;
  }
  >.ant-table-wrapper {
  .ant-table-wrapper {
    position: relative;
    z-index: 1;
  }
  .ant-table {
    color: inherit;
src/menu/components/search/main-search/dragsearch/card.jsx
@@ -118,11 +118,11 @@
    if (options.length === 0) {
      options = [{Value: '1', Text: '选项1'}, {Value: '2', Text: '选项2'}]
    }
    formItem = (<Radio.Group value={card.initval} style={{lineHeight: '36px'}}>
    formItem = (<Radio.Group value={card.initval} style={{lineHeight: '40px', whiteSpace: 'nowrap'}}>
      {options.map((item, i) => (<Radio key={i} value={item.Value}> {item.Text} </Radio>))}
    </Radio.Group>)
  } else if (card.type === 'check') {
    formItem = <Checkbox style={{lineHeight: '36px'}} checked={card.initval === card.openVal}>{card.checkTip || ''}</Checkbox>
    formItem = <Checkbox style={{lineHeight: '36px', whiteSpace: 'nowrap'}} checked={card.initval === card.openVal}>{card.checkTip || ''}</Checkbox>
  } else if (card.type === 'range') {
    let vals = card.initval.split(',')
    formItem = (<>
src/menu/components/search/main-search/index.jsx
@@ -354,9 +354,9 @@
  }
  getWrapForms = () => {
    const { wrap, action } = this.state.card
    const { card } = this.state
    return getWrapForm(wrap, action)
    return getWrapForm(card.wrap, card.uuid)
  }
  updateWrap = (res) => {
src/menu/components/search/main-search/options.jsx
@@ -1,7 +1,8 @@
import MenuUtils from '@/utils/utils-custom.js'
/**
 * @description Wrap表单配置信息
 */
export default function (wrap, action = []) {
export default function (wrap, uuid) {
  let roleList = sessionStorage.getItem('sysRoles')
  let appType = sessionStorage.getItem('appType')
@@ -14,6 +15,9 @@
  } else {
    roleList = []
  }
  let menu = window.GLOB.customMenu
  let modules = MenuUtils.getSupModules(menu.components, uuid, menu.interfaces)
  const wrapForm = [
    {
@@ -168,6 +172,15 @@
      forbid: !!appType
    },
    {
      type: 'cascader',
      field: 'supModule',
      label: '上级组件',
      initval: wrap.supModule || [],
      required: false,
      options: modules,
      forbid: sessionStorage.getItem('editMenuType') === 'popview'
    },
    {
      type: 'multiselect',
      field: 'blacklist',
      label: '黑名单',
src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -12,17 +12,18 @@
const { TextArea } = Input
const MkEditIcon = asyncComponent(() => import('@/components/mkIcon'))
const CodeMirror = asyncComponent(() => import('@/templates/zshare/codemirror'))
const MKTable = asyncComponent(() => import('@/components/normalform/modalform/mkTable'))
const acTyOptions = {
  pop: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'anchors', 'width', 'openmenu', 'refreshTab', 'position', 'tipTitle', 'hoverTitle', 'hidden', 'preButton', 'formCache'],
  prompt: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'anchors', 'width', 'openmenu', 'refreshTab', 'position', 'tipTitle', 'hoverTitle', 'hidden', 'preButton', 'formCache'],
  exec: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'anchors', 'width', 'openmenu', 'refreshTab', 'hoverTitle', 'hidden', 'preButton', 'formCache'],
  excelIn: ['label', 'Ot', 'OpenType', 'intertype', 'show', 'icon', 'class', 'color', 'sheet', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'width', 'hidden'],
  excelOut: ['label', 'Ot', 'OpenType', 'intertype', 'show', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'pagination', 'search', 'width', 'hidden'],
  popview: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'color', 'popClose', 'width', 'display', 'ratio', 'syncComponent', 'clickouter', 'maskStyle', 'closeButton', 'hidden'],
  tab: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'color', 'linkmenu', 'width', 'hidden', 'openTab'],
  innerpage: ['label', 'Ot', 'OpenType', 'pageTemplate', 'show', 'swipe', 'icon', 'class', 'color', 'width', 'hidden'],
  funcbutton: ['label', 'OpenType', 'funcType', 'show', 'swipe', 'icon', 'class', 'color', 'width', 'hidden'],
  pop: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'hover', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'anchors', 'width', 'openmenu', 'refreshTab', 'position', 'hoverTitle', 'hidden', 'preButton'],
  prompt: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'hover', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'anchors', 'width', 'openmenu', 'refreshTab', 'position', 'tipTitle', 'hoverTitle', 'hidden', 'preButton'],
  exec: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'hover', 'swipe', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'anchors', 'width', 'openmenu', 'refreshTab', 'hoverTitle', 'hidden', 'preButton'],
  excelIn: ['label', 'Ot', 'OpenType', 'intertype', 'show', 'hover', 'icon', 'class', 'color', 'sheet', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'width', 'hidden'],
  excelOut: ['label', 'Ot', 'OpenType', 'intertype', 'show', 'hover', 'icon', 'class', 'color', 'execSuccess', 'execError', 'syncComponent', 'switchTab', 'pagination', 'search', 'width', 'hidden'],
  popview: ['label', 'Ot', 'OpenType', 'show', 'hover', 'icon', 'class', 'color', 'popClose', 'width', 'display', 'ratio', 'syncComponent', 'clickouter', 'maskStyle', 'closeButton', 'hidden'],
  tab: ['label', 'Ot', 'OpenType', 'show', 'hover', 'icon', 'class', 'color', 'linkmenu', 'width', 'hidden', 'openTab'],
  innerpage: ['label', 'Ot', 'OpenType', 'pageTemplate', 'show', 'hover', 'swipe', 'icon', 'class', 'color', 'width', 'hidden'],
  funcbutton: ['label', 'OpenType', 'funcType', 'show', 'hover', 'swipe', 'icon', 'class', 'color', 'width', 'hidden'],
  form: ['label', 'OpenType', 'formType', 'intertype', 'Ot', 'execSuccess', 'execError', 'syncComponent', 'width', 'openmenu', 'refreshTab', 'title', 'hidden']
}
@@ -160,7 +161,7 @@
    let reRequired = {}
    let reReadonly = {}
    if (openType === 'pop' || openType === 'prompt' || openType === 'exec') {
    if (['pop', 'prompt', 'exec'].includes(openType)) {
      let intertype = this.record.intertype
      reOptions.intertype = this.state.interTypeOptions
@@ -214,7 +215,7 @@
          shows.push('exInterface', 'exProInterface')
        }
      } else if (intertype === 'inner') {
        shows.push('innerFunc', 'output', 'recordUser')
        shows.push('innerFunc', 'output', 'extraParam')
        if (Ot === 'requiredOnce') { // 前置函数
          shows.push('preFunc')
        }
@@ -295,7 +296,7 @@
          shows.push('exInterface', 'exProInterface')
        }
      } else if (intertype === 'inner') {
        shows.push('innerFunc', 'recordUser')
        shows.push('innerFunc', 'extraParam')
        reRequired.innerFunc = true
      } else {
        shows.push('sql', 'sqlType')
@@ -346,7 +347,7 @@
          shows.push('exInterface', 'exProInterface')
        }
      } else if (this.record.intertype === 'inner') {
        shows.push('innerFunc', 'recordUser')
        shows.push('innerFunc', 'extraParam')
        reRequired.innerFunc = true
      }
      if (this.record.execSuccess === 'grid' || this.record.execError === 'grid') {
@@ -381,7 +382,7 @@
          shows.push('exInterface', 'exProInterface')
        }
      } else if (this.record.intertype === 'inner') {
        shows.push('innerFunc', 'recordUser')
        shows.push('innerFunc', 'extraParam')
        reRequired.innerFunc = true
      }
      if (this.record.execSuccess === 'grid' || this.record.execError === 'grid') {
@@ -408,6 +409,16 @@
    } else if (openType === 'tab') {
      reOptions.Ot = requireOptions.filter(op => ['notRequired', 'requiredSgl', 'requiredOnce'].includes(op.value))
      if (Ot === 'notRequired') {
        shows.push('sysId')
      }
      if (shows.includes('linkmenu') && this.record.linkmenu[0] === 'multiMenu') {
        shows.push('multiMenus')
      } else if (this.record.sysId === 'js') {
        shows.push('sign')
      }
      reRequired.linkmenu = true
      reTooltip.linkmenu = ''
    } else if (openType === 'innerpage') {
@@ -425,7 +436,10 @@
        reRequired.linkmenu = true
        reTooltip.linkmenu = ''
      } else if (this.record.pageTemplate === 'billprint') {
        shows.push('printTemp')
        shows.push('printTemp', 'preHandle')
        if (this.record.preHandle === 'true') {
          shows.push('pre_func')
        }
        reOptions.Ot = requireOptions
      } else if (this.record.pageTemplate === 'pay') {
        reOptions.Ot = requireOptions.filter(op => op.value === 'requiredSgl')
@@ -459,7 +473,7 @@
            shows.push('exInterface', 'exProInterface')
          }
        } else if (this.record.intertype === 'inner') {
          shows.push('innerFunc', 'recordUser')
          shows.push('innerFunc', 'extraParam')
          reRequired.innerFunc = true
        }
        if (this.record.execSuccess === 'grid' || this.record.execError === 'grid') {
@@ -554,15 +568,21 @@
        shows.push('reason')
      }
    }
    if (shows.includes('syncComponent') && this.record.syncComponent[0] === 'multiComponent') {
      shows.push('syncComponents')
    }
    if (this.record.hidden !== 'true') {
      shows.push('permission')
    }
    if (this.record.show === 'icon') {
      reRequired.icon = true
    } else {
      reRequired.icon = false
      if (['pop', 'prompt', 'exec', 'popview', 'tab', 'innerpage'].includes(openType)) {
        shows.push('showName')
      }
    }
    return {
@@ -582,6 +602,7 @@
   * 3、切换标签类型,重置可选标签
   */
  optionChange = (key, value) => {
    const { type } = this.props
    const { hasclass, appType, requireOptions } = this.state
    this.record[key] = value
@@ -616,9 +637,16 @@
        _fieldval.label = '导出Excel'
        _fieldval.class = 'dgreen'
        _fieldval.execSuccess = 'never'
        _fieldval.Ot = 'requiredOnce'
        _fieldval.control = ''
        this.record.Ot = 'requiredOnce'
        if (type !== 'card') {
          _fieldval.Ot = 'requiredOnce'
          this.record.Ot = 'requiredOnce'
        } else {
          _fieldval.Ot = 'notRequired'
          this.record.Ot = 'notRequired'
        }
        this.record.label = '导出Excel'
        this.record.class = 'dgreen'
        this.record.execSuccess = 'never'
@@ -784,7 +812,7 @@
      let className = ''
      let content = null
      let initVal = item.initVal || ''
      let help = item.help || ''
      let help = item.help || null
      if (item.type === 'splitLine') {
        fields.push(
@@ -951,27 +979,53 @@
        ]
        content = <KeyInterface type={item.key === 'exInterface' ? 'develop' : 'product'}/>
      } else if (item.type === 'codemirror') {
        span = 24
        className = 'codemirror'
        rules = [
          { required: item.readonly ? false : item.required, message: '请输入' + item.label + '!' }
        ]
        content = <CodeMirror mode="text/javascript"/>
      }
      if (help && typeof(help) === 'function') {
        help = help(this.record)
      }
      fields.push(
        <Col span={span} key={index}>
          <Form.Item className={className} help={help} label={item.tooltip ?
            <Tooltip placement="topLeft" overlayStyle={{maxWidth: item.tooltip.length > 25 ? 350 : 250 }} title={<span onClick={(e) => e.stopPropagation()}>{item.tooltip}</span>}>
              <QuestionCircleOutlined className="mk-form-tip" />
              {item.label}
            </Tooltip> : item.label
          }>
            {getFieldDecorator(item.key, {
              initialValue: initVal,
              rules: rules
            })(content)}
          </Form.Item>
        </Col>
      )
      if (help) {
        fields.push(
          <Col span={span} key={index}>
            <Form.Item className={className} help={help} label={item.tooltip ?
              <Tooltip placement="topLeft" overlayStyle={{maxWidth: item.tooltip.length > 25 ? 350 : 250 }} title={<span onClick={(e) => e.stopPropagation()}>{item.tooltip}</span>}>
                <QuestionCircleOutlined className="mk-form-tip" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: initVal,
                rules: rules
              })(content)}
            </Form.Item>
          </Col>
        )
      } else {
        fields.push(
          <Col span={span} key={index}>
            <Form.Item className={className} label={item.tooltip ?
              <Tooltip placement="topLeft" overlayStyle={{maxWidth: item.tooltip.length > 25 ? 350 : 250 }} title={<span onClick={(e) => e.stopPropagation()}>{item.tooltip}</span>}>
                <QuestionCircleOutlined className="mk-form-tip" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: initVal,
                rules: rules
              })(content)}
            </Form.Item>
          </Col>
        )
      }
    })
    return fields
@@ -1078,14 +1132,32 @@
        
              let id = values.linkmenu[values.linkmenu.length - 1]
        
              list.forEach(item => {
                if (item.MenuID === id) {
                  values.MenuID = id
                  values.MenuName = item.MenuName
                  values.MenuNo = item.MenuNo
                  values.tabType = item.type
                }
              })
              if (id !== 'multiMenu') {
                list.forEach(item => {
                  if (item.MenuID === id) {
                    values.MenuID = id
                    values.MenuName = item.MenuName
                    values.MenuNo = item.MenuNo
                    values.tabType = item.type
                  }
                })
                delete values.multiMenus
              } else {
                values.multiMenus.forEach(menu => {
                  menu.sign = menu.sign || ''
                  let _id = menu.menuId[menu.menuId.length - 1]
                  list.forEach(item => {
                    if (item.MenuID === _id) {
                      menu.MenuID = _id
                      menu.MenuName = item.MenuName
                      menu.MenuNo = item.MenuNo
                      menu.tabType = item.type
                    }
                  })
                })
              }
            }
          } else if (values.OpenType === 'funcbutton' && values.funcType === 'expPdf') {
            values.Ot = 'notRequired'
@@ -1115,6 +1187,14 @@
            })
          }
          if (values.extraParam) {
            values.extraParam.forEach(n => {
              values[n] = 'true'
            })
            delete values.extraParam
          }
          resolve(values)
        } else {
          reject(err)
src/menu/components/share/actioncomponent/actionform/index.scss
@@ -17,6 +17,17 @@
      width: 86%;
    }
  }
  .codemirror {
    .ant-col-sm-7 {
      width: 14%;
    }
    .ant-col-sm-17 {
      width: 86%;
    }
    .ant-form-item-label {
      opacity: 0;
    }
  }
  .ant-radio-group {
    white-space: nowrap;
    .ant-radio-wrapper {
src/menu/components/share/actioncomponent/actionform/mkPrintTemps/index.jsx
@@ -74,6 +74,7 @@
        showSearch
        allowClear
        value={value}
        dropdownMatchSelectWidth={false}
        filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
        onSelect={this.selectChange}
        onChange={(val) => val === undefined && this.selectChange('')}
src/menu/components/share/actioncomponent/formconfig.jsx
@@ -8,7 +8,7 @@
 * @param {*} setting        组件配置
 * @param {*} usefulFields   存储过程可用的开始字段
 */
export function getActionForm (card, functip, config, usefulFields, modules = [], anchors = [], side) {
export function getActionForm (card, functip, config, usefulFields, modules = [], anchors = [], side, position) {
  let appType = sessionStorage.getItem('appType')
  let viewType = sessionStorage.getItem('editMenuType') // 弹窗 popview
  let setting = config.setting || {}
@@ -313,6 +313,29 @@
    card.formType = 'switch'
  }
  let width = card.width || (card.width === 0 ? 0 : 12)
  if (/x/.test(card.width)) {
    width = +width.replace(/x/, '.5')
  }
  let extraParam = []
  if (card.recordUser === 'true') {
    extraParam.push('recordUser')
  }
  if (card.dataM === 'true') {
    extraParam.push('dataM')
  }
  if (!appType) {
    if (typeof(card.openmenu) === 'string') {
      card.openmenu = []
    }
  } else {
    if (typeof(card.openmenu) !== 'string') {
      card.openmenu = ''
    }
  }
  let forms = [
    {
      type: 'select',
@@ -494,14 +517,14 @@
      initVal: card.linkmenu || (isApp ? '' : []),
      required: true,
      extendName: 'MenuNo',
      options: isApp ? appMenus : menulist
      options: isApp ? appMenus : (menulist.length ? [...menulist, {value: 'multiMenu', label: '多菜单'}] : [])
    },
    {
      type: 'text',
      key: 'prefix',
      label: '前缀',
      initVal: card.prefix || '',
      tooltip: '扫码信息将与前缀拼接后执行。注:跳转菜单需以mkbid开头。',
      tooltip: '扫码信息将与前缀拼接后执行。注:跳转菜单需以mkbid(:或,)开头,mkbid(:或,)跳转后将被去除。例如:mkbid:123456 跳转后页面BID为 123456。',
      required: false
    },
    {
@@ -809,10 +832,10 @@
      key: 'width',
      min: 0,
      max: 24,
      precision: 0,
      precision: 1,
      label: '宽度',
      initVal: card.width || (card.width === 0 ? 0 : 12),
      tooltip: '栅格布局,每行等分为24列。为 0 时宽度自适应。',
      initVal: width,
      tooltip: '栅格布局,每行等分为24列。为 0 时宽度自适应。可设置半列即.5。',
      forbid: type !== 'card',
      required: true
    },
@@ -847,6 +870,25 @@
      }, {
        value: 'link',
        text: '文字+图标'
      }]
    },
    {
      type: 'radio',
      key: 'hover',
      label: '悬浮效果',
      initVal: card.hover || '',
      tooltip: '鼠标悬浮按钮上方时的颜色变化。',
      required: false,
      forbid: type === 'card' || appType === 'mob',
      options: [{
        value: '',
        text: '无'
      }, {
        value: 'mk-btn-hover-bg',
        text: '背景变化'
      }, {
        value: 'mk-btn-hover-border',
        text: '边框变化'
      }]
    },
    {
@@ -1029,14 +1071,14 @@
      tooltip: '执行成功后的返回值。系统函数可指定返回的变量(以@符开头,返回id时可使用@id@);自定义函数可指定返回字段(如id)。',
      initVal: card.output || '',
      required: false,
      forbid: viewType === 'popview'
      // forbid: viewType === 'popview'
    },
    {
      type: 'text',
      key: 'tipTitle',
      label: '确认提示',
      initVal: card.tipTitle || '',
      tooltip: '注:弹窗(表单)在显示为是否框时有效。',
      tooltip: '提示框的确认提示信息。',
      required: false
    },
    {
@@ -1047,6 +1089,20 @@
      tooltip: '鼠标悬浮在按钮上方时的提示信息。',
      forbid: appType === 'mob',
      required: false
    },
    {
      type: 'select',
      key: 'showName',
      label: '显示内容',
      initVal: card.showName || '',
      tooltip: '行级按钮可通过行信息控制按钮显示内容。',
      required: false,
      allowClear: true,
      forbid: position !== 'line',
      options: columns.map(item => ({
        value: item.field,
        text: `${item.label}(${item.field})`
      }))
    },
    {
      type: 'radio',
@@ -1221,18 +1277,18 @@
      forbid: appType === 'mob'
    },
    {
      type: 'radio',
      key: 'recordUser',
      label: '记录用户',
      initVal: card.recordUser || 'false',
      tooltip: '当选择“是”时,内部函数的传参会增加 username 与 fullname。',
      type: 'checkbox',
      key: 'extraParam',
      label: '扩展参数',
      initVal: extraParam,
      tooltip: '选择“用户信息”时,内部函数的传参会增加 username 与 fullname。选择“数据管理员”时,内部函数的传参会增加 dataM ,管理员值为“Y”,普通用户为空。',
      required: false,
      options: [{
        value: 'false',
        text: '否'
        value: 'recordUser',
        text: '用户信息'
      }, {
        value: 'true',
        text: '是'
        value: 'dataM',
        text: '数据管理员'
      }]
    },
    {
@@ -1282,21 +1338,21 @@
      initVal: card.reason || '',
      required: false
    },
    {
      type: 'radio',
      key: 'formCache',
      label: '表单缓存',
      initVal: card.formCache || 'false',
      tooltip: '主要用于数据修改后,更新相关表单的选项,清空缓存后表单再次打开时数据会重新加载。',
      required: false,
      options: [{
        value: 'false',
        text: '不清空'
      }, {
        value: 'clear',
        text: '清空'
      }]
    },
    // {
    //   type: 'radio',
    //   key: 'formCache',
    //   label: '表单缓存',
    //   initVal: card.formCache || 'false',
    //   tooltip: '主要用于数据修改后,更新相关表单的选项,清空缓存后表单再次打开时数据会重新加载。',
    //   required: false,
    //   options: [{
    //     value: 'false',
    //     text: '不清空'
    //   }, {
    //     value: 'clear',
    //     text: '清空'
    //   }]
    // },
    {
      type: 'radio',
      key: 'hidden',
@@ -1311,6 +1367,18 @@
        value: 'true',
        text: '是'
      }]
    },
    {
      type: 'radio',
      key: 'permission',
      label: '权限验证',
      initVal: card.permission || 'true',
      required: false,
      options: [
        {value: 'true', text: '继承菜单'},
        {value: 'false', text: '禁用'},
      ],
      forbid: viewType === 'popview'
    },
    {
      type: 'splitLine',
@@ -1457,6 +1525,87 @@
          options: modules
        }
      ]
    },
    {
      type: 'radio',
      key: 'sysId',
      label: '自定义ID',
      initVal: card.sysId || '',
      tooltip: '不选行按钮可在前端生成ID值(32位),作为后续菜单的BID,存在标记时,ID值后将拼接标记值。',
      required: false,
      options: [{
        value: '',
        text: '空'
      }, {
        value: 'js',
        text: '前端生成'
      }]
    },
    {
      type: 'text',
      key: 'sign',
      label: '标记',
      initVal: card.sign || '',
      required: false
    },
    {
      type: 'table',
      key: 'multiMenus',
      label: '菜单列表',
      initVal: card.multiMenus || [],
      required: true,
      actions: ['edit', 'del', 'add', 'move'],
      columns: [
        {
          title: '名称',
          dataIndex: 'name',
          inputType: 'text',
          editable: true,
          required: false,
          width: '30%'
        },
        {
          title: '菜单',
          dataIndex: 'menuId',
          inputType: 'cascader',
          editable: true,
          required: true,
          extends: [{key: 'label', value: 'label', mutilLabel: 'name'}],
          width: '30%',
          render: (text, record) => record.label,
          options: menulist
        },
        {
          title: '标记',
          dataIndex: 'sign',
          inputType: 'text',
          editable: true,
          required: false,
          width: '20%'
        }
      ]
    },
    {
      type: 'radio',
      key: 'preHandle',
      label: '自定义脚本',
      initVal: card.preHandle || 'false',
      // tooltip: '隐藏后按钮在页面中不显示,且不参与权限分配。',
      required: false,
      options: [{
        value: 'false',
        text: '禁用'
      }, {
        value: 'true',
        text: '启用'
      }]
    },
    {
      type: 'codemirror',
      key: 'pre_func',
      label: '自定义脚本',
      initVal: card.pre_func || '',
      required: true,
    }
  ]
@@ -1470,7 +1619,7 @@
 * @param {*} setting        组件配置
 * @param {*} usefulFields   存储过程可用的开始字段
 */
export function getBaseTableActionForm (card, functip, config, usefulFields, modules) {
export function getBaseTableActionForm (card, functip, config, usefulFields, modules, position) {
  let viewType = sessionStorage.getItem('editMenuType') // 弹窗 popview
  let setting = config.setting || {}
  let columns = config.columns || []
@@ -1559,6 +1708,14 @@
  if (card.OpenType === 'form') { // 拖拽添加类型转换
    card.OpenType = 'pop'
  }
  let extraParam = []
  if (card.recordUser === 'true') {
    extraParam.push('recordUser')
  }
  if (card.dataM === 'true') {
    extraParam.push('dataM')
  }
  let forms = [
@@ -1721,7 +1878,7 @@
      initVal: card.linkmenu || [],
      required: true,
      extendName: 'MenuNo',
      options: menulist
      options: menulist.length ? [...menulist, {value: 'multiMenu', label: '多菜单'}] : []
    },
    {
      type: 'textarea',
@@ -2063,6 +2220,24 @@
      }]
    },
    {
      type: 'radio',
      key: 'hover',
      label: '悬浮效果',
      initVal: card.hover || '',
      tooltip: '鼠标悬浮按钮上方时的颜色变化。',
      required: false,
      options: [{
        value: '',
        text: '无'
      }, {
        value: 'mk-btn-hover-bg',
        text: '背景变化'
      }, {
        value: 'mk-btn-hover-border',
        text: '边框变化'
      }]
    },
    {
      type: 'icon',
      key: 'icon',
      label: '图标',
@@ -2166,14 +2341,14 @@
      tooltip: '执行成功后的返回值。系统函数可指定返回的变量(以@符开头,返回id时可使用@id@);自定义函数可指定返回字段(如id)。',
      initVal: card.output || '',
      required: false,
      forbid: viewType === 'popview'
      // forbid: viewType === 'popview'
    },
    {
      type: 'text',
      key: 'tipTitle',
      label: '确认提示',
      initVal: card.tipTitle || '',
      tooltip: '注:弹窗(表单)在显示为是否框时有效。',
      tooltip: '提示框的确认提示信息。',
      required: false
    },
    {
@@ -2183,6 +2358,20 @@
      initVal: card.hoverTitle || '',
      tooltip: '鼠标悬浮在按钮上方时的提示信息。',
      required: false
    },
    {
      type: 'select',
      key: 'showName',
      label: '显示内容',
      initVal: card.showName || '',
      tooltip: '行级按钮可通过行信息控制按钮显示内容。',
      required: false,
      allowClear: true,
      forbid: position !== 'line',
      options: columns.map(item => ({
        value: item.field,
        text: `${item.label}(${item.field})`
      }))
    },
    {
      type: 'radio',
@@ -2279,18 +2468,18 @@
      required: false
    },
    {
      type: 'radio',
      key: 'recordUser',
      label: '记录用户',
      initVal: card.recordUser || 'false',
      tooltip: '当选择“是”时,内部函数的传参会增加 username 与 fullname。',
      type: 'checkbox',
      key: 'extraParam',
      label: '扩展参数',
      initVal: extraParam,
      tooltip: '选择“用户信息”时,内部函数的传参会增加 username 与 fullname。选择“数据管理员”时,内部函数的传参会增加 dataM ,管理员值为“Y”,普通用户为空。',
      required: false,
      options: [{
        value: 'false',
        text: '否'
        value: 'recordUser',
        text: '用户信息'
      }, {
        value: 'true',
        text: '是'
        value: 'dataM',
        text: '数据管理员'
      }]
    },
    {
@@ -2340,21 +2529,21 @@
      initVal: card.reason || '',
      required: false
    },
    {
      type: 'radio',
      key: 'formCache',
      label: '表单缓存',
      initVal: card.formCache || 'false',
      tooltip: '主要用于数据修改后,更新相关表单的选项,清空缓存后表单再次打开时数据会重新加载。',
      required: false,
      options: [{
        value: 'false',
        text: '不清空'
      }, {
        value: 'clear',
        text: '清空'
      }]
    },
    // {
    //   type: 'radio',
    //   key: 'formCache',
    //   label: '表单缓存',
    //   initVal: card.formCache || 'false',
    //   tooltip: '主要用于数据修改后,更新相关表单的选项,清空缓存后表单再次打开时数据会重新加载。',
    //   required: false,
    //   options: [{
    //     value: 'false',
    //     text: '不清空'
    //   }, {
    //     value: 'clear',
    //     text: '清空'
    //   }]
    // },
    {
      type: 'radio',
      key: 'hidden',
@@ -2369,6 +2558,18 @@
        value: 'true',
        text: '是'
      }]
    },
    {
      type: 'radio',
      key: 'permission',
      label: '权限验证',
      initVal: card.permission || 'true',
      required: false,
      options: [
        {value: 'true', text: '启用'},
        {value: 'false', text: '禁用'},
      ],
      forbid: viewType === 'popview'
    },
    {
      type: 'radio',
@@ -2420,6 +2621,87 @@
          options: modules
        }
      ]
    },
    {
      type: 'radio',
      key: 'sysId',
      label: '自定义ID',
      initVal: card.sysId || '',
      tooltip: '不选行按钮可在前端生成ID值(32位),作为后续菜单的BID,存在标记时,ID值后将拼接标记值。',
      required: false,
      options: [{
        value: '',
        text: '空'
      }, {
        value: 'js',
        text: '前端生成'
      }]
    },
    {
      type: 'text',
      key: 'sign',
      label: '标记',
      initVal: card.sign || '',
      required: false
    },
    {
      type: 'table',
      key: 'multiMenus',
      label: '菜单列表',
      initVal: card.multiMenus || [],
      required: true,
      actions: ['edit', 'del', 'add', 'move'],
      columns: [
        {
          title: '名称',
          dataIndex: 'name',
          inputType: 'text',
          editable: true,
          required: false,
          width: '30%'
        },
        {
          title: '菜单',
          dataIndex: 'menuId',
          inputType: 'cascader',
          editable: true,
          required: true,
          extends: [{key: 'label', value: 'label', mutilLabel: 'name'}],
          width: '30%',
          render: (text, record) => record.label,
          options: menulist
        },
        {
          title: '标记',
          dataIndex: 'sign',
          inputType: 'text',
          editable: true,
          required: false,
          width: '20%'
        }
      ]
    },
    {
      type: 'radio',
      key: 'preHandle',
      label: '自定义脚本',
      initVal: card.preHandle || 'false',
      // tooltip: '隐藏后按钮在页面中不显示,且不参与权限分配。',
      required: false,
      options: [{
        value: 'false',
        text: '禁用'
      }, {
        value: 'true',
        text: '启用'
      }]
    },
    {
      type: 'codemirror',
      key: 'pre_func',
      label: '自定义脚本',
      initVal: card.pre_func || '',
      required: true,
    }
  ]
src/menu/components/share/actioncomponent/index.jsx
@@ -120,7 +120,17 @@
    const { card, actionlist } = this.state
    let _card = fromJS(card).toJS()
    _card.style = style
    _card.style = fromJS(style).toJS()
    if (_card.style.paddingLeft === '15px') {
      delete _card.style.paddingLeft
    }
    if (_card.style.paddingRight === '15px') {
      delete _card.style.paddingRight
    }
    if (_card.style.minHeight === '28px') {
      delete _card.style.minHeight
    }
    let _actionlist = actionlist.map(cell => {
      if (cell.uuid === _card.uuid) return _card
@@ -137,6 +147,10 @@
  changeBtnStyle = (element) => {
    let _style = element.style ? fromJS(element.style).toJS() : {}
    let options = ['font', 'border', 'background', 'margin', 'padding', 'minHeight']
    _style.paddingLeft = _style.paddingLeft || '15px'
    _style.paddingRight = _style.paddingRight || '15px'
    _style.minHeight = _style.minHeight || '28px'
    this.setState({
      card: element
@@ -718,6 +732,7 @@
          destroyOnClose
        >
          <ActionForm
            type={config.type === 'tree' ? 'card' : ''}
            card={card}
            formlist={this.state.formlist}
            inputSubmit={this.handleSubmit}
src/menu/components/share/normalheader/index.jsx
@@ -36,11 +36,22 @@
  }
  getStyle = (style) => {
    if (!style.borderBottomWidth) {
      style.borderBottomWidth = '0px'
    let _style = fromJS(style).toJS()
    if (!_style.borderBottomWidth) {
      _style.borderBottomWidth = '0px'
    }
    if (_style.fontFamily) {
      if (_style.fontFamily.length === 0) {
        delete _style.fontFamily
      } else {
        _style.fontFamily = _style.fontFamily.join(',')
      }
    }
    if (_style.lineHeight === 2.8) {
      delete _style.lineHeight
    }
    let _config = {...this.props.config, headerStyle: style}
    let _config = {...this.props.config, headerStyle: _style}
    
    this.props.updateComponent(_config)
  }
@@ -48,9 +59,13 @@
  changeStyle = () => {
    const { config } = this.props
    let options = ['font', 'border', 'background', 'padding']
    let _style = config.headerStyle ? fromJS(config.headerStyle).toJS() : {}
    let options = ['font', 'border', 'background', 'padding', 'fontFamily']
    MKEmitter.emit('changeStyle', options, config.headerStyle, this.getStyle)
    _style.fontFamily = _style.fontFamily ? _style.fontFamily.split(',') : []
    _style.lineHeight = _style.lineHeight || 2.8
    MKEmitter.emit('changeStyle', options, _style, this.getStyle)
  }
  render() {
src/menu/components/share/normalheader/index.scss
@@ -12,6 +12,7 @@
    text-decoration: inherit;
    font-weight: inherit;
    font-style: inherit;
    font-family: inherit;
    float: left;
    line-height: inherit;
    margin-left: 10px;
@@ -41,6 +42,10 @@
    border: 1px solid #d9d9d9;
    opacity: 0.6;
  }
  .ant-input-search.ant-input-group-wrapper {
    position: relative;
    top: 5px;
  }
}
.normal-header:not(.tree-search) {
  display: flex;
src/menu/components/share/searchcomponent/dragsearch/card.jsx
@@ -1,6 +1,6 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Select, DatePicker, Input, Popover, Form, Switch, Checkbox } from 'antd'
import { Select, DatePicker, Input, Popover, Form, Switch, Checkbox, Radio } from 'antd'
import { EditOutlined, CopyOutlined, CloseOutlined } from '@ant-design/icons'
import moment from 'moment'
@@ -105,15 +105,23 @@
      format={format}
      className="data-range"
      placeholder={['BeginTime', 'EndTime']}
      renderExtraFooter={() => 'extra footer'}
      renderExtraFooter={() => 'extra footer'}
      value={_defaultValue}
    />
  } else if (card.type === 'group') {
    formItem = <DateGroup card={card} />
  } else if (card.type === 'switch') {
    formItem = (<Switch checkedChildren={card.openText || ''} unCheckedChildren={card.closeText || ''} style={{marginTop: '8px'}} checked={card.initval === card.openVal}/>)
  } else if (card.type === 'radio') {
    let options = card.options
    if (options.length === 0) {
      options = [{Value: '1', Text: '选项1'}, {Value: '2', Text: '选项2'}]
    }
    formItem = (<Radio.Group value={card.initval} style={{lineHeight: '40px', whiteSpace: 'nowrap'}}>
      {options.map((item, i) => (<Radio key={i} value={item.Value}> {item.Text} </Radio>))}
    </Radio.Group>)
  } else if (card.type === 'check') {
    formItem = <Checkbox style={{lineHeight: '36px'}} checked={card.initval === card.openVal}>{card.checkTip || ''}</Checkbox>
    formItem = <Checkbox style={{lineHeight: '36px', whiteSpace: 'nowrap'}} checked={card.initval === card.openVal}>{card.checkTip || ''}</Checkbox>
  } else if (card.type === 'range') {
    type = 'range-wrap'
    let vals = card.initval.split(',')
src/menu/components/share/searchcomponent/index.jsx
@@ -93,6 +93,7 @@
   * @description 搜索条件编辑,获取搜索条件表单信息
   */
  handleSearch = (card) => {
    const { config } = this.props
    const { searchlist } = this.state
    let linkableFields = []
@@ -107,10 +108,17 @@
      })
    })
    let columns = null
    if (config.columns && config.columns.length) {
      columns = config.columns.map(item => {
        return {key: item.uuid, text: item.field, value: item.field, label: item.label}
      })
    }
    this.setState({
      visible: true,
      card: card,
      formlist: getSearchForm(card, linkableFields, [], 'header')
      formlist: getSearchForm(card, linkableFields, columns, 'header')
    })
  }
src/menu/components/share/searchcomponent/index.scss
@@ -1,9 +1,14 @@
.model-custom-header-search-list {
  padding: 0px;
  .ant-row >.ant-col {
    float: right;
    padding: 0 6px;
  >.ant-row {
    text-align: right;
    >.ant-col {
      display: inline-block;
      float: none;
      padding: 0 6px;
      text-align: left;
    }
  }
  .page-card {
src/menu/components/table/base-table/columns/editColumn/index.jsx
@@ -91,7 +91,7 @@
    if (this.record.type === 'text' || this.record.type === 'number') {
      if (this.record.perspective === 'linkmenu') {
        _options.push('linkmenu', 'linkfields', 'open')
        _options.push('linkmenu', 'open')
      } else if (this.record.perspective === 'linkurl') {
        _options.push('linkurl', 'linkfields', 'open')
      }
src/menu/components/table/base-table/options.jsx
@@ -20,6 +20,17 @@
    },
    {
      type: 'radio',
      field: 'parity',
      label: '奇偶异色',
      initval: wrap.parity || 'false',
      required: false,
      options: [
        {value: 'true', label: '启用'},
        {value: 'false', label: '禁用'},
      ]
    },
    {
      type: 'radio',
      field: 'bordered',
      label: '边框',
      initval: wrap.bordered || 'true',
@@ -36,8 +47,8 @@
      initval: wrap.actionfixed !== 'true' ? 'false' : 'true',
      required: false,
      options: [
        {value: 'true', label: '是'},
        {value: 'false', label: '否'},
        {value: 'true', label: '启用'},
        {value: 'false', label: '禁用'},
      ]
    },
    {
@@ -64,20 +75,6 @@
        {value: 'small', label: '小'},
        {value: 'mini', label: '迷你'},
      ]
    },
    {
      type: 'select',
      field: 'selected',
      label: '数据选中',
      initval: wrap.selected || 'false',
      tooltip: '初始化:数据加载时选中首行数据,仅执行一次。数据加载:每次数据加载时均选中首行(当按钮执行完成并返回主键值时,默认选中主键值对应行)。选中标记:返回数据中存在 selected 字段,且值为 true 的数据被选中。注:启用无人值守时无效。',
      required: false,
      options: [
        {value: 'false', label: '无'},
        {value: 'init', label: '初始化'},
        {value: 'always', label: '数据加载'},
        {value: 'sign', label: '选中标记'}
      ],
    },
    // {
    //   type: 'radio',
@@ -138,15 +135,18 @@
      ]
    },
    {
      type: 'number',
      field: 'btnlimit',
      label: '按钮限制',
      initval: wrap.btnlimit || '',
      tooltip: '按钮数量限制,超出的按钮会在更多中下拉显示,注:更多中的按钮不要绑定双击事件。',
      min: 3,
      max: 3000,
      precision: 0,
      required: false
      type: 'select',
      field: 'selected',
      label: '数据选中',
      initval: wrap.selected || 'false',
      tooltip: '初始化:数据加载时选中首行数据,仅执行一次。数据加载:每次数据加载时均选中首行(当按钮执行完成并返回主键值时,默认选中主键值对应行)。选中标记:返回数据中存在 selected 字段,且值为 true 的数据被选中。注:启用无人值守时无效。',
      required: false,
      options: [
        {value: 'false', label: '无'},
        {value: 'init', label: '初始化'},
        {value: 'always', label: '数据加载'},
        {value: 'sign', label: '选中标记'}
      ],
    },
    {
      type: 'number',
@@ -211,6 +211,17 @@
    },
    {
      type: 'number',
      field: 'btnlimit',
      label: '按钮限制',
      initval: wrap.btnlimit || '',
      tooltip: '按钮数量限制,超出的按钮会在更多中下拉显示,注:更多中的按钮不要绑定双击事件。',
      min: 3,
      max: 3000,
      precision: 0,
      required: false
    },
    {
      type: 'number',
      field: 'maxPageSize',
      label: '每页最大数',
      initval: wrap.maxPageSize || '',
src/menu/components/table/edit-table/columns/tableIn/customscript/index.jsx
@@ -266,7 +266,7 @@
          </Col> : null}
          {!type ? <Col span={10}>
            <Form.Item label="报错字段" style={{margin: 0, whiteSpace: 'nowrap'}}>
              ErrorCode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT), retmsg
              errorcode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT), retmsg
            </Form.Item>
          </Col> : null}
          {!type ? <Col span={24} className="sqlfield">
src/menu/components/table/edit-table/columns/tableIn/index.scss
@@ -52,10 +52,6 @@
  .custom-table .ant-empty {
    margin: 20px 8px!important;
  }
  .excel-custom-table {
    position: relative;
    top: -25px;
  }
  .errorval {
    display: inline-block;
    width: 30px;
src/menu/components/table/edit-table/options.jsx
@@ -129,7 +129,18 @@
        {value: 'default', label: '大'},
        {value: 'middle', label: '中'},
        {value: 'small', label: '小'},
        {value: 'mini', label: '迷你'},
        // {value: 'mini', label: '迷你'},
      ]
    },
    {
      type: 'radio',
      field: 'parity',
      label: '奇偶异色',
      initval: wrap.parity || 'false',
      required: false,
      options: [
        {value: 'true', label: '启用'},
        {value: 'false', label: '禁用'},
      ]
    },
    {
@@ -193,7 +204,7 @@
      initval: wrap.permission || (!appType ? 'true' : 'false'),
      required: false,
      options: [
        {value: 'true', label: '启用'},
        {value: 'true', label: !appType ? '继承菜单' : '启用'},
        {value: 'false', label: '禁用'},
      ],
      forbid: sessionStorage.getItem('editMenuType') === 'popview'
src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx
@@ -61,6 +61,9 @@
    value: 'colspan',
    text: '合并列'
  }, {
    value: 'extend',
    text: '扩展列'
  }, {
    value: 'index',
    text: '序号'
  }]
@@ -197,6 +200,95 @@
      }]
    },
    {
      type: 'radio',
      key: 'colUnit',
      label: '单位',
      initVal: card.colUnit || 'day',
      required: true,
      options: [{
        value: 'day',
        text: '天'
      }, {
        value: 'hour',
        text: '小时'
      }]
    },
    {
      type: 'number',
      key: 'shift',
      label: '偏移量',
      initVal: card.shift || 0,
      min: -1000,
      max: 1000,
      decimal: 0,
      required: true
    },
    {
      type: 'number',
      key: 'quota',
      label: '指标数',
      initVal: card.quota || 7,
      min: 1,
      max: 1000,
      decimal: 0,
      required: true
    },
    {
      type: 'select',
      key: 'dayFormat',
      label: '格式化',
      initVal: card.dayFormat || 'M/DD',
      required: true,
      options: [{
        value: 'M/DD',
        label: 'M/DD(4/29)'
      }, {
        value: 'M-DD',
        label: 'M-DD(4-29)'
      }, {
        value: 'M月DD日',
        label: 'M月DD日(4月29日)'
      }, {
        value: 'M/DD week',
        label: 'M/DD week(4/29 星期一)'
      }, {
        value: 'M-DD week',
        label: 'M-DD week(4-29 星期一)'
      }, {
        value: 'M月DD日 week',
        label: 'M月DD日 week(4月29日 星期一)'
      }]
    },
    {
      type: 'select',
      key: 'hourFormat',
      label: '格式化',
      initVal: card.hourFormat || 'H:00',
      required: true,
      options: [{
        value: 'H:00',
        label: 'H:00(15:00)'
      }, {
        value: 'H point',
        label: 'H(15点)'
      }, {
        value: 'h:00',
        label: 'h:00(3:00 pm)'
      }]
    },
    {
      type: 'text',
      key: 'supField',
      label: '上级字段',
      initVal: card.supField || '',
      tooltip: '来源于上级组件的字段集(上级组件为空时从url参数中选取),该字段值(用逗号分隔)可控制扩展列的列名。',
      required: false,
      rules: [{
        pattern: /^[\u4E00-\u9FA50-9a-zA-Z_]*$/ig,
        message: '字段名只允许包含数字、字母、汉字以及_'
      }]
    },
    {
      type: 'number',
      key: 'startTime',
      precision: 0,
src/menu/components/table/normal-table/columns/editColumn/index.jsx
@@ -20,6 +20,7 @@
  custom: ['label', 'type', 'Align', 'Width', 'blacklist', 'IsSort'],
  action: ['label', 'type', 'Align', 'Width'],
  formula: ['label', 'type', 'Align', 'Hide', 'Width', 'prefix', 'postfix', 'eval', 'formula', 'blacklist'],
  extend: ['label', 'field', 'type', 'Align', 'Width', 'colUnit', 'shift', 'quota', 'supField'],
  index: ['label', 'type', 'Align', 'Width']
}
@@ -89,7 +90,7 @@
    if (this.record.type === 'text' || this.record.type === 'number') {
      if (this.record.perspective === 'linkmenu') {
        _options.push('linkmenu', 'linkfields', 'open')
        _options.push('linkmenu', 'open')
      } else if (this.record.perspective === 'linkurl') {
        _options.push('linkurl', 'linkfields', 'open')
      }
@@ -97,6 +98,12 @@
      _options.push('decimal')
    } else if (this.record.type === 'custom' && this.record.IsSort === 'true') {
      _options.push('sortField')
    } else if (this.record.type === 'extend') {
      if (this.record.colUnit === 'day') {
        _options.push('dayFormat')
      } else {
        _options.push('hourFormat')
      }
    }
    if (this.record.Hide !== 'true') {
@@ -186,7 +193,7 @@
      }
    } else if (key === 'format' && value === 'percent') {
      this.props.form.setFieldsValue({postfix: '%'})
    } else if (['perspective', 'eval', 'IsSort', 'textFormat'].includes(key)) {
    } else if (['perspective', 'eval', 'IsSort', 'textFormat', 'colUnit'].includes(key)) {
      let _options = this.getOptions()
      this.setState({
@@ -211,12 +218,12 @@
      if (item.hidden || item.forbid) return
      if (item.type === 'text') {
        let rules = []
        let rules = item.rules || []
        if (item.key !== 'linkurl') {
          rules = [{
          rules.push({
            max: formRule.input.max,
            message: formRule.input.message
          }]
          })
        }
        fields.push(
          <Col span={12} key={index}>
@@ -356,7 +363,7 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'cascader') { // 多选
      } else if (item.type === 'cascader') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
src/menu/components/table/normal-table/index.jsx
@@ -278,11 +278,11 @@
  }
  getWrapForms = () => {
    const { wrap, action, columns, cols } = this.state.card
    const { card } = this.state
    let _actions = []
    cols.forEach(col => {
    card.cols.forEach(col => {
      if (col.type === 'custom') {
        col.elements.forEach(cell => {
          if (cell.eleType !== 'button') return
@@ -292,7 +292,7 @@
      }
    })
    return getWrapForm(wrap, _actions, columns, action)
    return getWrapForm(card.wrap, _actions, card.columns, card.action, card.supNodes || [], card.uuid)
  }
  updateWrap = (res) => {
@@ -311,7 +311,21 @@
    res.borderRadius = card.wrap.borderRadius || 0
    res.resetContrl = card.wrap.resetContrl || 'init'
    this.updateComponent({...card, wrap: res})
    let _card = {...card, wrap: res}
    if (res.supNodes) {
      _card.supNodes = res.supNodes
      _card.supNodes = _card.supNodes.map(item => {
        item.componentId = item.nodes[item.nodes.length - 1]
        return item
      })
      delete res.supNodes
    } else {
      delete _card.supNodes
    }
    this.updateComponent(_card)
  }
  updatecolumn = (config) => {
src/menu/components/table/normal-table/options.jsx
@@ -1,7 +1,9 @@
import MenuUtils from '@/utils/utils-custom.js'
/**
 * @description Wrap表单配置信息
 */
export default function (wrap, action = [], columns = [], toolBtns) {
export default function (wrap, action = [], columns = [], toolBtns, supNodes, id = '') {
  let roleList = sessionStorage.getItem('sysRoles')
  let appType = sessionStorage.getItem('appType')
  let isprint = sessionStorage.getItem('MenuType') === 'billPrint'
@@ -16,6 +18,9 @@
  } else {
    roleList = []
  }
  let menu = window.GLOB.customMenu
  let modules = MenuUtils.getSupModules(menu.components, id, menu.interfaces)
  const wrapForm = [
    {
@@ -69,6 +74,17 @@
      ],
      controlFields: [
        {field: 'selected', values: ['radio', 'checkbox']},
      ]
    },
    {
      type: 'radio',
      field: 'parity',
      label: '奇偶异色',
      initval: wrap.parity || 'false',
      required: false,
      options: [
        {value: 'true', label: '启用'},
        {value: 'false', label: '禁用'},
      ]
    },
    {
@@ -155,20 +171,6 @@
      ]
    },
    {
      type: 'select',
      field: 'selected',
      label: '数据选中',
      initval: wrap.selected || 'false',
      tooltip: '初始化:数据加载时选中首行数据,仅执行一次。数据加载:每次数据加载时均选中首行(当按钮执行完成并返回主键值时,默认选中主键值对应行)。选中标记:返回数据中存在 selected 字段,且值为 true 的数据被选中。',
      required: false,
      options: [
        {value: 'false', label: '无'},
        {value: 'init', label: '初始化'},
        {value: 'always', label: '数据加载'},
        {value: 'sign', label: '选中标记'}
      ],
    },
    {
      type: 'color',
      field: 'borderColor',
      label: '边框颜色',
@@ -190,16 +192,18 @@
      ]
    },
    {
      type: 'number',
      field: 'btnlimit',
      label: '按钮限制',
      initval: wrap.btnlimit || '',
      tooltip: '按钮数量限制,超出的按钮会在更多中下拉显示,注:更多中的按钮不要绑定双击事件。',
      min: 3,
      max: 3000,
      precision: 0,
      type: 'select',
      field: 'selected',
      label: '数据选中',
      initval: wrap.selected || 'false',
      tooltip: '初始化:数据加载时选中首行数据,仅执行一次。数据加载:每次数据加载时均选中首行(当按钮执行完成并返回主键值时,默认选中主键值对应行)。选中标记:返回数据中存在 selected 字段,且值为 true 的数据被选中。',
      required: false,
      forbid: appType !== '' || isprint
      options: [
        {value: 'false', label: '无'},
        {value: 'init', label: '初始化'},
        {value: 'always', label: '数据加载'},
        {value: 'sign', label: '选中标记'}
      ],
    },
    {
      type: 'select',
@@ -254,6 +258,18 @@
    },
    {
      type: 'number',
      field: 'btnlimit',
      label: '按钮限制',
      initval: wrap.btnlimit || '',
      tooltip: '按钮数量限制,超出的按钮会在更多中下拉显示,注:更多中的按钮不要绑定双击事件。',
      min: 3,
      max: 3000,
      precision: 0,
      required: false,
      forbid: appType !== '' || isprint
    },
    {
      type: 'number',
      field: 'maxPageSize',
      label: '每页最大数',
      initval: wrap.maxPageSize || '',
@@ -288,6 +304,22 @@
        {value: 'false', label: '忽略'},
      ],
      forbid: isprint
    },
    {
      type: 'radio',
      field: 'supType',
      label: '上级类型',
      initval: wrap.supType || 'single',
      tooltip: '上级组件为单一组件或多个组件。',
      required: false,
      forbid: appType === 'mob' || isprint,
      options: [
        {value: 'single', label: '单组件'},
        {value: 'multi', label: '多组件'},
      ],
      controlFields: [
        {field: 'supNodes', values: ['multi']},
      ]
    },
    {
      type: 'radio',
@@ -335,6 +367,36 @@
      options: roleList,
      forbid: !!appType || isprint
    },
    {
      type: 'table',
      field: 'supNodes',
      label: '上级组件',
      initval: supNodes,
      required: true,
      forbid: appType === 'mob' || isprint,
      span: 24,
      actions: ['edit', 'del', 'add', 'move'],
      columns: [
        {
          title: '序号',
          dataIndex: '$index',
          editable: false,
          required: false,
          width: '20%'
        },
        {
          title: '组件',
          dataIndex: 'nodes',
          inputType: 'cascader',
          editable: true,
          required: true,
          extends: [{key: 'label', value: 'label'}],
          width: '50%',
          render: (text, record) => record.label,
          options: modules
        }
      ]
    }
  ]
  return wrapForm
src/menu/components/timeline/normal-timeline/index.scss
@@ -23,7 +23,7 @@
  }
  .card-item {
    overflow: hidden;
    // overflow: hidden;
    position: relative;
    background-color: #ffffff;
    background-position: center center;
src/menu/components/tree/antd-tree/index.jsx
@@ -15,6 +15,7 @@
const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
const PasteComponent = asyncIconComponent(() => import('@/components/paste'))
const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
@@ -88,6 +89,7 @@
    card.name = card.wrap.name
    card.$c_ds = true
    card.$c_ac = true
      
    card.errors = checkComponent(card)
@@ -160,6 +162,20 @@
    MKEmitter.emit('addButton', card.uuid, newcard)
  }
  pasteComponent = (res, resolve) => {
    if (res.style) {
      delete res.style.width
      delete res.style.float
    }
    res.Ot = res.Ot === 'requiredSgl' ? 'requiredSgl' : 'notRequired'
    resolve({status: true})
    res.uuid = Utils.getuuid()
    this.addButton(res)
  }
  setSubConfig = (item) => {
    const { card, appType } = this.state
    let btn = fromJS(item).toJS()
@@ -180,9 +196,9 @@
  }
  getWrapForms = () => {
    const { wrap, columns } = this.state.card
    const { wrap, columns, action } = this.state.card
    return getWrapForm(wrap, columns)
    return getWrapForm(wrap, columns, action.findIndex(item => item.Ot === 'requiredSgl') > -1)
  }
  updateWrap = (res) => {
@@ -205,6 +221,9 @@
  render() {
    const { card } = this.state
    let _style = resetStyle(card.style)
    if (card.wrap.lineHeight) {
      _style['--mk-tree-line-height'] = card.wrap.lineHeight + 'px'
    }
    return (
      <div className="menu-tree-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
@@ -217,6 +236,7 @@
              <EditOutlined style={{color: '#1890ff'}} title="编辑"/>
            </NormalForm>
            <CopyComponent type="tree" card={card}/>
            <PasteComponent options={['action']} updateConfig={this.pasteComponent} />
            <FontColorsOutlined className="style" title="调整样式" onClick={this.changeStyle}/>
            <ClockComponent config={card} updateConfig={this.updateComponent}/>
            <DeleteOutlined className="close" title="删除组件" onClick={() => this.props.deletecomponent(card.uuid)} />
src/menu/components/tree/antd-tree/index.scss
@@ -7,6 +7,7 @@
  background-size: cover;
  min-height: 100px;
  overflow-y: auto;
  --mk-tree-line-height: 24px;
  .anticon-tool {
    position: absolute;
@@ -28,6 +29,15 @@
  .model-menu-action-list:not(.length0) {
    margin: 10px 0px;
  }
  .model-menu-action-list {
    .ant-btn {
      border-width: 0px;
    }
  }
  .ant-tree li .ant-tree-node-content-wrapper, .ant-tree li span.ant-tree-switcher, .ant-tree li span.ant-tree-iconEle {
    height: var(--mk-tree-line-height);
    line-height: var(--mk-tree-line-height);
  }
}
.menu-tree-box::after {
  display: block;
src/menu/components/tree/antd-tree/options.jsx
@@ -1,7 +1,7 @@
/**
 * @description Wrap表单配置信息
 */
export default function (wrap, columns = []) {
export default function (wrap, columns = [], hasLineAction) {
  let roleList = sessionStorage.getItem('sysRoles')
  let appType = sessionStorage.getItem('appType')
  let ispop = sessionStorage.getItem('editMenuType') === 'popview'
@@ -79,6 +79,16 @@
      required: true
    },
    {
      type: 'number',
      field: 'lineHeight',
      label: '行高',
      initval: wrap.lineHeight || 24,
      min: 24,
      max: 200,
      precision: 0,
      required: true
    },
    {
      type: 'radio',
      field: 'showIcon',
      label: '图标',
@@ -124,6 +134,19 @@
    },
    {
      type: 'radio',
      field: 'actShow',
      label: '按钮显示',
      initval: wrap.actShow || 'dropdown',
      tooltip: '选择单行按钮的显示方式。',
      required: false,
      forbid: !hasLineAction,
      options: [
        {value: 'dropdown', label: '下拉'},
        {value: 'line', label: '行内'},
      ]
    },
    {
      type: 'radio',
      field: 'permission',
      label: '权限验证',
      initval: wrap.permission || 'false',
src/menu/datasource/verifycard/customscript/index.jsx
@@ -226,7 +226,7 @@
          </Col> : null}
          <Col span={18}>
            <Form.Item label="报错字段" style={{margin: 0}}>
              ErrorCode, retmsg
              errorcode, retmsg
              <span style={{marginLeft: 25}}>
                成功:
                <span className="error-val" onClick={() => {this.showError('S')}}> S </span>、
src/menu/datasource/verifycard/index.jsx
@@ -48,6 +48,7 @@
    systemScripts: [],
    median: {},
    visible: false,
    editLineId: '',
    pvisible: false,
    reload: false,
    script: null,
@@ -677,7 +678,8 @@
      if (config.subtype === 'dualdatacard') {
        _columns = [...columns, ...subColumns]
      }
      let r = SettingUtils.getDebugSql(setting, _scripts, _columns, searches, config.type)
      let r = SettingUtils.getDebugSql(setting, _scripts, _columns, searches, config.subtype)
      let _debugId = md5(r.sql)
      
@@ -809,7 +811,9 @@
      return
    }
    let editLineId = ''
    if (script) {
      editLineId = script.uuid
      _scripts = _scripts.map(item => {
        if (script.uuid === item.uuid) {
          item.sql = scriptValue
@@ -824,10 +828,11 @@
        status: 'true'
      }
      editLineId = _script.uuid
      _scripts.push(_script)
    }
    this.setState({loading: true})
    this.setState({loading: true, editLineId})
    this.sqlverify(() => {this.setState({scripts: _scripts, script: null, scriptValue: '', loading: false})}, () => {this.setState({loading: false})}, 'script', _scripts)
  }
@@ -954,7 +959,8 @@
  }
  copyColumns = () => {
    const { columns } = this.state
    const { columns, setting } = this.state
    let m = []
    let n = []
    let s = []
@@ -978,8 +984,29 @@
    }
    let oInput = document.createElement('input')
    oInput.value = `/*${m.join(',')}*/
      ${n.join(',')}`
    oInput.value = `create table #${setting.tableName || 'tb'}
    (${m.join(',')},sort_id INT IDENTITY(1,1))
    insert into #${setting.tableName || 'tb'}
    (${n.join(',')})
    select ${n.join(',')}
    from ${setting.dataresource ? `(${setting.dataresource.replace(/\n/g, ' ')}) tb` : setting.tableName || 'tb'}
    order by @orderBy@
    declare @mk_total int
    set @mk_total = 0
    select @mk_total = count(1) from #${setting.tableName || 'tb'}
      declare @pageIndex_top int
      set @pageIndex_top=(@pageIndex@-1)*@pageSize@
      if @mk_total > @pageIndex@*@pageSize@
        delete #${setting.tableName || 'tb'} where sort_id > @pageIndex@*@pageSize@
      if @pageIndex_top > 0
        delete #${setting.tableName || 'tb'} where sort_id <= @pageIndex_top
    drop table #${setting.tableName || 'tb'}`
    document.body.appendChild(oInput)
    oInput.select()
    document.execCommand('Copy')
@@ -1106,7 +1133,7 @@
  render() {
    const { config } = this.props
    const { columns, subColumns, median, setting, scripts, colColumns, scriptsColumns, activeKey, loading, searches, defaultsql, visible, pvisible, reload, script, scriptValue, searchKey } = this.state
    const { columns, subColumns, median, setting, scripts, colColumns, scriptsColumns, activeKey, loading, searches, defaultsql, visible, pvisible, reload, script, scriptValue, searchKey, editLineId } = this.state
    return (
      <div className="model-data-source-wrap">
@@ -1130,7 +1157,7 @@
              wrappedComponentRef={(inst) => this.settingForm = inst}
            /> : null}
          </TabPane>
          {config.subtype !== 'basetable' ? <TabPane tab={
          {!['basetable', 'invoice', 'invTable'].includes(config.subtype) ? <TabPane tab={
            <span>
              字段集
              {columns.length ? <span className="count-tip">{columns.length}</span> : null}
@@ -1186,7 +1213,7 @@
                })
                return
              }
              this.setState({visible: true, script: null, scriptValue: ''})
              this.setState({visible: true, script: null, scriptValue: '', editLineId: ''})
            }}/> : null}
            <CustomScriptsForm
              type={config.type}
@@ -1236,8 +1263,14 @@
                  </div>
                )
              } else {
                let sign = ''
                if (script && script.uuid === item.uuid) {
                  sign = 'active'
                } else if (editLineId === item.uuid) {
                  sign = 'edited'
                }
                return (
                  <div className={'script-item ' + (script && script.uuid === item.uuid ? 'active' : '') } key={item.uuid}>
                  <div className={'script-item ' + sign} key={item.uuid}>
                    <div style={{cursor: 'pointer'}} onClick={() => {
                      this.setState({script: item, scriptValue: item.sql})
                    }}>
src/menu/datasource/verifycard/index.scss
@@ -195,6 +195,11 @@
        .script-item.active {
          background-color: #bae7ff;
        }
        .script-item.edited {
          .ant-typography {
            color: #1890ff;
          }
        }
        .ant-typography {
          margin-bottom: 5px;
        }
src/menu/datasource/verifycard/settingform/index.jsx
@@ -375,7 +375,7 @@
              </Form.Item>
            </Col> : null}
            {setting.interType === 'system' ? <Col span={24} className="data-source" style={{paddingLeft: '7px'}}>
              {window.GLOB.process ? <span className="process-btn" onClick={this.addProcess}>工作流</span> : null}
              {window.GLOB.process && !['invoice', 'invTable'].includes(config.subtype) ? <span className="process-btn" onClick={this.addProcess}>工作流</span> : null}
              <Form.Item labelCol={{xs: { span: 24 }, sm: { span: 2 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 22 }} } label={
                <Tooltip placement="topLeft" title={`使用系统函数时,需填写数据源。注:数据权限替换符 $@ -> /* 或 ''、 @$ -> */ 或 ''。`}>
                  <QuestionCircleOutlined className="mk-form-tip" />
@@ -409,7 +409,7 @@
                </Radio.Group>)}
              </Form.Item>
            </Col> : null}
            <Col span={8}>
            {!['invTable'].includes(config.subtype) ? <Col span={8}>
              <Form.Item label="主键">
                {getFieldDecorator('primaryKey', {
                  initialValue: setting.primaryKey || ''
@@ -423,7 +423,7 @@
                  </Select>
                )}
              </Form.Item>
            </Col>
            </Col> : null}
            {/* 数组数据,需设置排序规则 */}
            {config.format === 'array' ? <Col span={8}>
              <Form.Item label={
@@ -454,7 +454,7 @@
                </Radio.Group>)}
              </Form.Item>
            </Col> : null}
            {!['navbar', 'balcony', 'menubar'].includes(config.type) && (!config.wrap || config.wrap.supType !== 'multi') && MenuType !== 'billPrint' ? <Col span={8}>
            {!['balcony', 'menubar', 'commonbar', 'tabbar', 'invTable'].includes(config.subtype) && (!config.wrap || config.wrap.supType !== 'multi') && MenuType !== 'billPrint' ? <Col span={8}>
              <Form.Item label={
                <Tooltip placement="topLeft" title={'该组件如果受其他组件控制,请选项相应的组件,没有时选“无”。'}>
                  <QuestionCircleOutlined className="mk-form-tip" />
@@ -516,7 +516,7 @@
              </Form.Item>
            </Col> : null}
            {/* 1、不分页且不存在上级模块 */}
            {!['navbar', 'interface', 'calendar'].includes(config.type) && !['editable', 'basetable', 'dualdatacard'].includes(config.subtype) && (!config.pageable || (config.pageable && setting.laypage === 'false')) && (setting.supModule.length === 0 || setting.supModule[0] === 'empty') && setting.interType === 'system' && setting.onload !== 'false' ? <Col span={8}>
            {!['navbar', 'interface', 'calendar'].includes(config.type) && !['editable', 'basetable', 'dualdatacard', 'invoice', 'invTable'].includes(config.subtype) && (!config.pageable || (config.pageable && setting.laypage === 'false')) && (setting.supModule.length === 0 || setting.supModule[0] === 'empty') && setting.interType === 'system' && setting.onload !== 'false' ? <Col span={8}>
              <Form.Item label={
                <Tooltip placement="topLeft" title={'初始化加载时,是否与其他组件一同加载数据,注:如菜单未使用后端缓存,则查询语句大于8000字符时无效。'}>
                  <QuestionCircleOutlined className="mk-form-tip" />
@@ -533,7 +533,7 @@
                )}
              </Form.Item>
            </Col> : null}
            {!['navbar', 'balcony', 'menubar'].includes(config.type) && !['basetable'].includes(config.subtype) ? <Col span={8}>
            {!['navbar', 'balcony', 'menubar'].includes(config.type) && !['basetable', 'invoice', 'invTable'].includes(config.subtype) ? <Col span={8}>
              <Form.Item label={
                <Tooltip placement="topLeft" title={config.type === 'interface' ? '单独搜索组件可作为公共数据源的搜索条件。' : '优先使用同级的搜索条件组件,同级搜索不存在时,依次向上选取,与当前组件的搜索条件一同用作数据过滤(当前组件的搜索条件优先)。'}>
                  <QuestionCircleOutlined className="mk-form-tip" />
@@ -683,8 +683,7 @@
          wrapClassName="mk-flow-type"
          visible={visible}
          width={400}
          maskClosable={false}
          closable={false}
          onCancel={() => this.setState({visible: false})}
          footer={[
            <Button key="cancel" className="mk-green" onClick={() => this.execAddProcess('flowstart')}>发起</Button>,
            <Button key="confirm" className="mk-primary" onClick={() => this.execAddProcess('flowcheck')}>审批</Button>
src/menu/datasource/verifycard/utils.jsx
@@ -75,7 +75,7 @@
      {reg: /@sum\$|\$sum@/ig, value: ''},
    ]
    if (window.GLOB.process) {
    if (window.GLOB.process && type !== 'invoice') {
      regs.push({reg: /@works_flow_code@/ig, value: `'${getuuid()}'`})
    }
@@ -101,7 +101,7 @@
      `
    }
    if (window.GLOB.urlFields) {
    if (window.GLOB.urlFields && type !== 'invoice') {
      window.GLOB.urlFields.forEach(field => {
        let reg = new RegExp('@' + field + '@', 'ig')
        _dataresource = _dataresource.replace(reg, `'0'`)
src/menu/debug/index.jsx
@@ -770,7 +770,7 @@
    }
    _sql = `Declare @tbid nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@BillCode nvarchar(50),@BVoucher nvarchar(50),@FIBVoucherDate nvarchar(50), @FiYear nvarchar(50),@ModularDetailCode nvarchar(50), @UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@mk_departmentcode nvarchar(512),@mk_organization nvarchar(512),@mk_user_type nvarchar(20),@mk_nation nvarchar(50),@mk_province nvarchar(50),@mk_city nvarchar(50),@mk_district nvarchar(50),@mk_address nvarchar(100),@mk_deleted int,@bid nvarchar(50)${_declarefields}
      `
    let userName = 'User_Name'
    let fullName = 'Full_Name'
    let RoleID = 'role_id'
@@ -1281,15 +1281,46 @@
      let detailId = '0'
  
      if (verify.flowSql === 'true') {
        _sql += `
        /* 工作流默认sql */
        insert into s_my_works_flow (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,work_group,works_flow_detail_id,work_grade,bid,createuserid,CreateUser,CreateStaff,upid)
        select @ID@,@works_flow_code@,@works_flow_name@,@works_flow_param@,@status@,@statusname@,@work_group@,@works_flow_detail_id@,@work_grade@,@bid@,@UserID@,@UserName,@FullName,@time_id@
        insert into s_my_works_flow_log (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,works_flow_detail_id,work_group,work_grade,upid)
        select @ID@,@works_flow_code@,@works_flow_name@ ,@works_flow_param@,@status@,@statusname@,@works_flow_detail_id@,@work_group@,@work_grade@,@time_id@
        insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
        select @ID@,@works_flow_code@,@works_flow_detail_id@,@userid@,@start_type@,@userid@,@UserName,@FullName,@time_id@
        `
        if (verify.flowType === 'start') {
          _sql += `
            /* 工作流默认sql */
            insert into s_my_works_flow (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,work_group,works_flow_detail_id,work_grade,bid,createuserid,CreateUser,CreateStaff,upid)
            select @ID@,@works_flow_code@,@works_flow_name@,@works_flow_param@,@status@,@statusname@,@work_group@,@works_flow_detail_id@,@work_grade@,@bid@,@UserID@,@UserName,@FullName,@time_id@
            insert into s_my_works_flow_log (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,works_flow_detail_id,work_group,work_grade,upid)
            select @ID@,@works_flow_code@,@works_flow_name@ ,@works_flow_param@,@status@,@statusname@,@works_flow_detail_id@,@work_group@,@work_grade@,@time_id@
            insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
            select @ID@,@works_flow_code@,@works_flow_detail_id@,@userid@,@start_type@,@userid@,@UserName,@FullName,@time_id@
          `
        } else {
          _sql += `
            /* 工作流默认sql */
            update s_my_works_flow set status=@status@,statusname=@statusname@,works_flow_param=@works_flow_param@,works_flow_detail_id=@works_flow_detail_id@,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
            where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
            insert into s_my_works_flow_log (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,works_flow_detail_id,work_group,work_grade,upid)
            select @ID@,@works_flow_code@,@works_flow_name@ ,@works_flow_param@,@status@,@statusname@,@works_flow_detail_id@,@work_group@,@work_grade@,@time_id@
            update s_my_works_flow_role set deleted=10,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
            where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
            if @check_userids@ != ''
            begin
                  insert into s_my_works_flow_role (works_flow_id,works_flow_code,userid,works_flow_detail_id,createuserid,CreateUser,CreateStaff,upid)
                  select @ID@,@works_flow_code@,ID,@works_flow_detail_id@,@userid@,@UserName,@FullName,@time_id@ from dbo.SplitComma(@check_userids@)
                  insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
                  select @ID@,@works_flow_code@,@works_flow_detail_id@,ID,@check_type@,@userid@,@UserName,@FullName,@time_id@ from dbo.SplitComma(@check_userids@)
            end
            if @notice_userids@ != ''
            begin
                  update n
                  set deleted=10,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
                  from (select * from s_my_works_flow_notice where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0) n
                  inner join (select ID from dbo.SplitComma(@notice_userids@)) s
                  on n.userid = s.id
                  insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
                  select @ID@,@works_flow_code@,@works_flow_detail_id@,ID,@notice_type@,@userid@,@UserName,@FullName,@time_id@ from dbo.SplitComma(@notice_userids@)
            end
          `
        }
      }
  
      _sql = _sql.replace(/@start_type@/ig, `'开始'`)
@@ -1494,7 +1525,7 @@
      `
    }
    sql = `create table #${sheet} (${declarefields.join(',')},jskey nvarchar(50),BID nvarchar(50) )
    sql = `create table #${sheet} (${declarefields.join(',')},jskey nvarchar(50),BID nvarchar(50))
      Declare @UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@mk_departmentcode nvarchar(512),@mk_organization nvarchar(512),@mk_user_type nvarchar(20),@mk_nation nvarchar(50),@mk_province nvarchar(50),@mk_city nvarchar(50),@mk_district nvarchar(50),@mk_address nvarchar(100),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
      
      Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @mk_departmentcode='${departmentcode}', @mk_organization='${organization}', @mk_user_type='${mk_user_type}', @mk_nation='${nation}', @mk_province='${province}', @mk_city='${city}', @mk_district='${district}', @mk_address='${address}'
@@ -1709,11 +1740,10 @@
    let item = {setting: {}, columns: [], search: []}
    btn.verify.columns.forEach(col => {
      if (col.Column && col.Column !== '$Index') {
        item.columns.push({
          field: col.Column
        })
      }
      if (col.output === 'false' || !col.Column || col.Column === '$Index') return
      item.columns.push({
        field: col.Column
      })
    })
    if (btn.verify.useSearch !== 'false') {
src/menu/header/index.jsx
@@ -7,15 +7,17 @@
class MenuHeader extends Component {
  state = {
    avatar: sessionStorage.getItem('CloudAvatar') || avatar,
    userName: sessionStorage.getItem('CloudUserName')
    userName: sessionStorage.getItem('CloudUserName'),
    logo: sessionStorage.getItem('CloudLogo') || MainLogo
  }
  render () {
    const { menuName } = this.props
    const { logo } = this.state
    return (
      <header className="menu-header-container">
        <div className="header-logo"><img src={MainLogo} alt=""/></div>
        <div className="header-logo"><img src={logo} alt=""/></div>
        <div className="menu-name">{menuName}</div>
        <div className="header-setting">
          <img src={this.state.avatar} alt=""/>
src/menu/menushell/card.jsx
@@ -30,6 +30,7 @@
const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline'))
const Voucher = asyncComponent(() => import('@/menu/components/module/voucher'))
const Account = asyncComponent(() => import('@/menu/components/module/account'))
const Invoice = asyncComponent(() => import('@/menu/components/module/invoice'))
const Iframe = asyncComponent(() => import('@/menu/components/iframe'))
const AntvG6 = asyncComponent(() => import('@/menu/components/chart/antv-G6'))
const AntvX6 = asyncComponent(() => import('@/menu/components/chart/antv-X6'))
@@ -135,6 +136,8 @@
      return (<Voucher card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'module' && card.subtype === 'account') {
      return (<Account card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'module' && card.subtype === 'invoice') {
      return (<Invoice card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    }
  }
src/menu/modalconfig/index.jsx
@@ -135,7 +135,7 @@
        })
      }
      if (item.type === 'switch' || item.type === 'check') {
      if (['switch', 'check', 'popSelect'].includes(item.type)) {
        _linksupFields.push({
          field: item.field,
          label: item.label
src/menu/modalconfig/index.scss
@@ -76,8 +76,16 @@
    height: 100%;
    overflow-y: hidden;
    background: #ffffff;
    .ant-card-head {
      min-height: 44px;
    .ant-card {
      padding-top: 45px;
      .ant-card-head {
        min-height: 44px;
        position: fixed;
        width: calc(100vw - 235px);
        top: 48px;
        z-index: 12;
        background: #ffffff;
      }
    }
    .ant-card-head-title {
      padding: 5px 0;
src/menu/modulecell/index.jsx
@@ -22,6 +22,7 @@
          { value: 'qrcode', text: '二维码', type: 'action', class: 'element', $init: true},
          { value: 'currentDate', text: '当前时间', type: 'action', class: 'element', $init: true},
          { value: 'formula', text: '公式', type: 'action', class: 'element', $init: true},
          { value: 'tag', text: '标签', type: 'action', class: 'element', $init: true},
          { value: 'color', text: '颜色', type: 'action', class: 'element', $init: true},
          { value: 'sequence', text: '序号', type: 'action', class: 'element', $init: true }
        ]
src/menu/modulesource/option.jsx
@@ -32,6 +32,7 @@
import Voucher from '@/assets/mobimg/voucher.png'
import Account from '@/assets/mobimg/account.png'
import canlendar from '@/assets/mobimg/canlendar.png'
import Invoice from '@/assets/img/invoice.png'
// 组件配置信息
export const menuOptions = [
@@ -72,4 +73,5 @@
  { type: 'menu', url: Iframe, component: 'iframe', subtype: 'iframe', title: 'iframe', width: 24, forbid: ['billPrint'] },
  { type: 'menu', url: Account, component: 'module', subtype: 'account', title: '账套', width: 24, forbid: ['billPrint'] },
  { type: 'menu', url: Voucher, component: 'module', subtype: 'voucher', title: '凭证', width: 24, forbid: ['billPrint'] },
  { type: 'menu', url: Invoice, component: 'module', subtype: 'invoice', title: '发票', width: 24, forbid: ['billPrint'] },
]
src/menu/stylecontroller/index.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import { is, fromJS } from 'immutable'
import { Collapse, Form, Col, InputNumber, Input, Select, Radio, Drawer, Button, message } from 'antd'
import { Collapse, Form, Col, InputNumber, Input, Select, Radio, Drawer, Button, message, Checkbox } from 'antd'
import {
  ColumnHeightOutlined,
  FontSizeOutlined,
@@ -53,7 +53,8 @@
    backgroundImage: '',
    options: [],
    borposition: 'outer',
    type: ''
    type: '',
    logo: sessionStorage.getItem('CloudLogo') || MainLogo
  }
  callback = null
@@ -130,30 +131,37 @@
  onCloseDrawer = () => {
    let card = fromJS(this.state.card).toJS()
    let check = false
    if (card.borderWidth === '0px') {
    if (card.borderWidth === '0px' || !card.borderWidth) {
      delete card.borderWidth
      delete card.borderColor
      check = true
    } else if (card.borderLeftWidth === '0px') {
      delete card.borderLeftWidth
      delete card.borderLeftColor
      check = true
    } else if (card.borderRightWidth === '0px') {
      delete card.borderRightWidth
      delete card.borderRightColor
      check = true
    } else if (card.borderTopWidth === '0px') {
      delete card.borderTopWidth
      delete card.borderTopColor
      check = true
    } else if (card.borderBottomWidth === '0px') {
      delete card.borderBottomWidth
      delete card.borderBottomColor
      check = true
    }
    if (check) {
    if (card.borderLeftWidth === '0px' || !card.borderLeftWidth) {
      delete card.borderLeftWidth
      delete card.borderLeftColor
    }
    if (card.borderRightWidth === '0px' || !card.borderRightWidth) {
      delete card.borderRightWidth
      delete card.borderRightColor
    }
    if (card.borderTopWidth === '0px' || !card.borderTopWidth) {
      delete card.borderTopWidth
      delete card.borderTopColor
    }
    if (card.borderBottomWidth === '0px' || !card.borderBottomWidth) {
      delete card.borderBottomWidth
      delete card.borderBottomColor
    }
    if (/0px 0px 0px | transparent/.test(card.boxShadow)) {
      delete card.hShadow
      delete card.vShadow
      delete card.shadowBlur
      delete card.shadowColor
      delete card.boxShadow
    }
    if (!is(fromJS(this.state.card), fromJS(card))) {
      this.callback && this.callback(card)
    }
@@ -285,7 +293,7 @@
  changeShadowColor = (val) => {
    const { card } = this.state
    let boxShadow = `${card.hShadow || '0px'} ${card.vShadow || '0px'} ${card.shadowBlur || '0px'} ${val}`
    let boxShadow = `${card.hShadow || '0px'} ${card.vShadow || '0px'} ${card.shadowBlur || '0px'} ${val || 'transparent'}`
    this.updateStyle({shadowColor: val, boxShadow})
  }
@@ -561,7 +569,7 @@
  }
  render () {
    const { card, options, backgroundImage, borposition, fonts, type } = this.state
    const { card, options, backgroundImage, borposition, fonts, type, logo } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -577,7 +585,7 @@
      <Drawer
        title={
          <div className="header-logo">
            <img src={MainLogo} alt=""/>
            <img src={logo} alt=""/>
          </div>
        }
        placement="left"
@@ -716,6 +724,25 @@
                      <Radio.Button value="line-through"><StrikethroughOutlined title="中划线"/></Radio.Button>
                      <Radio.Button value="overline" style={{textDecoration: 'overline'}}><span title="上划线">O</span></Radio.Button>
                    </Radio.Group>
                  </Form.Item>
                </Col> : null}
                {options.includes('fontFamily') ? <Col span={24}>
                  <Form.Item
                    colon={false}
                    label=" "
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Checkbox.Group options={[
                      { label: '微软雅黑', value: 'Microsoft YaHei' },
                      { label: '宋体', value: 'Simsun' },
                      { label: '黑体', value: 'Simhei' },
                      { label: '仿宋', value: 'FangSong' },
                      { label: '楷体', value: 'KaiTi' },
                      // { label: 'Helvetica', value: 'Helvetica' },
                      // { label: 'Arial', value: 'Arial' },
                      // { label: 'Verdana', value: 'Verdana' },
                      // { label: 'Georgia', value: 'Georgia' },
                    ]} defaultValue={card.fontFamily} onChange={(val) => this.changeNormalStyle(val, 'fontFamily')} />
                  </Form.Item>
                </Col> : null}
              </Panel> : null}
@@ -865,11 +892,11 @@
                    label={<ColumnWidthOutlined title="边框宽度"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    {borposition === 'outer' ? <StyleInput defaultValue={card.borderWidth || '0px'} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'left' ? <StyleInput defaultValue={card.borderLeftWidth || '0px'} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'right' ? <StyleInput defaultValue={card.borderRightWidth || '0px'} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'top' ? <StyleInput defaultValue={card.borderTopWidth || '0px'} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'bottom' ? <StyleInput defaultValue={card.borderBottomWidth || '0px'} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'outer' ? <StyleInput defaultValue={card.borderWidth || ''} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'left' ? <StyleInput defaultValue={card.borderLeftWidth || ''} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'right' ? <StyleInput defaultValue={card.borderRightWidth || ''} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'top' ? <StyleInput defaultValue={card.borderTopWidth || ''} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'bottom' ? <StyleInput defaultValue={card.borderBottomWidth || ''} options={['px']} onChange={this.changeBorderWidth}/> : null}
                  </Form.Item>
                </Col>
                <Col span={24}>
@@ -878,11 +905,11 @@
                    label={<BgColorsOutlined title="边框颜色"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    {borposition === 'outer' ? <ColorSketch value={card.borderColor || ''} onChange={this.changeBorderColor} /> : null}
                    {borposition === 'left' ? <ColorSketch value={card.borderLeftColor || ''} onChange={this.changeBorderColor} /> : null}
                    {borposition === 'right' ? <ColorSketch value={card.borderRightColor || ''} onChange={this.changeBorderColor} /> : null}
                    {borposition === 'top' ? <ColorSketch value={card.borderTopColor || ''} onChange={this.changeBorderColor} /> : null}
                    {borposition === 'bottom' ? <ColorSketch value={card.borderBottomColor || ''} onChange={this.changeBorderColor} /> : null}
                    {borposition === 'outer' ? <ColorSketch allowClear={true} value={card.borderColor || ''} onChange={this.changeBorderColor} /> : null}
                    {borposition === 'left' ? <ColorSketch allowClear={true} value={card.borderLeftColor || ''} onChange={this.changeBorderColor} /> : null}
                    {borposition === 'right' ? <ColorSketch allowClear={true} value={card.borderRightColor || ''} onChange={this.changeBorderColor} /> : null}
                    {borposition === 'top' ? <ColorSketch allowClear={true} value={card.borderTopColor || ''} onChange={this.changeBorderColor} /> : null}
                    {borposition === 'bottom' ? <ColorSketch allowClear={true} value={card.borderBottomColor || ''} onChange={this.changeBorderColor} /> : null}
                  </Form.Item>
                  <Form.Item
                    colon={false}
@@ -909,7 +936,7 @@
                    label={<BgColorsOutlined title="阴影颜色"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <ColorSketch value={card.shadowColor || ''} onChange={this.changeShadowColor} />
                    <ColorSketch allowClear={true} value={card.shadowColor || ''} onChange={this.changeShadowColor} />
                  </Form.Item>
                  <Form.Item
                    colon={false}
src/menu/stylecontroller/index.scss
@@ -127,6 +127,22 @@
                  }
                }
              }
              .ant-checkbox-group {
                margin-top: 10px;
                .ant-checkbox-wrapper {
                  color: rgba(255, 255, 255, 0.85);
                  margin-right: 4px;
                  margin-bottom: 2px;
                  .ant-checkbox + span {
                    padding-left: 6px;
                  }
                  .ant-checkbox:not(.ant-checkbox-checked) {
                    .ant-checkbox-inner {
                      background-color: rgba(255, 255, 255, 0.85);
                    }
                  }
                }
              }
            }
          }
        }
src/mob/components/topbar/normal-navbar/options.jsx
@@ -184,7 +184,7 @@
      field: 'prefix',
      label: '前缀',
      initval: wrap.prefix || '',
      tooltip: '扫码信息将与前缀拼接后执行。注:跳转菜单需以mkbid开头。',
      tooltip: '扫码信息将与前缀拼接后执行。注:跳转菜单需以mkbid(:或,)开头,mkbid(:或,)跳转后将被去除。例如:mkbid:123456 跳转后页面BID为 123456。',
      required: false
    },
    {
src/mob/header/index.jsx
@@ -7,13 +7,16 @@
class MobHeader extends Component {
  state = {
    avatar: sessionStorage.getItem('CloudAvatar') || avatar,
    userName: sessionStorage.getItem('CloudUserName')
    userName: sessionStorage.getItem('CloudUserName'),
    logo: sessionStorage.getItem('CloudLogo') || MainLogo
  }
  render () {
    const { logo } = this.state
    return (
      <header className="mob-header-container">
        <div className="header-logo"><img src={MainLogo} alt=""/></div>
        <div className="header-logo"><img src={logo} alt=""/></div>
        <div className="header-user">
          <img src={this.state.avatar} alt=""/>
          <span>
src/tabviews/basetable/index.jsx
@@ -213,18 +213,6 @@
      config.components.forEach(component => {
        if (component.type === 'tabs') return
        if (param.$searchkey) {
          component.search = component.search.map(item => {
            if (['text', 'select', 'link', 'checkcard'].includes(item.type) && param.$searchkey === item.field) {
              item.initval = param.$searchval
            }
            return item
          })
          component.$searches = Utils.initMainSearch(component.search)
        }
        if (component.$searches.length) {
          component.$main = true
          
@@ -358,6 +346,14 @@
      // 搜索条件初始化
      Utils.initSearchVal(item)
      if (urlparam.$searchkey) {
        item.search.forEach(cell => {
          if (urlparam.$searchkey === cell.field.toLowerCase() && ['text', 'select', 'link', 'checkcard'].includes(cell.type)) {
            cell.initval = urlparam.$searchval
          }
        })
      }
      item.$searches = Utils.initMainSearch(item.search)
      let statFields = []
@@ -397,7 +393,7 @@
                  cell = this.getPrinter(cell, item.uuid)
                }
                return skip || permAction[cell.uuid]
                return skip || permAction[cell.uuid] || cell.permission === 'false'
              } else if (['text', 'number', 'formula'].includes(cell.eleType)) {
                if (!cell.height) {
                  cell.innerHeight = 'auto'
@@ -410,8 +406,19 @@
                }
              }
              if (cell.linkmenu && cell.linkmenu.length > 0) {
                let menu_id = cell.linkmenu.pop()
                cell.linkThdMenu = window.GLOB.mkThdMenus.get(menu_id) || ''
                if (!cell.linkThdMenu) {
                  cell.link = ''
                }
              }
              if (cell.marks && cell.marks.length === 0) {
                cell.marks = null
              }
              if (cell.anchors && cell.anchors.length === 0) {
                cell.anchors = null
              }
              return true
@@ -453,7 +460,7 @@
            cell = this.getPrinter(cell, item.uuid)
          }
          return skip || permAction[cell.uuid]
          return skip || permAction[cell.uuid] || cell.permission === 'false'
        })
      }
      
src/tabviews/commontable/index.jsx
@@ -209,7 +209,7 @@
      // 字段透视及必填标志
      config.search = config.search.map(item => {
        if (['text', 'select', 'link', 'checkcard'].includes(item.type) && param && param.$searchkey === item.field) {
        if (['text', 'select', 'link', 'checkcard'].includes(item.type) && param && param.$searchkey === item.field.toLowerCase()) {
          item.initval = param.$searchval
        }
        return item
@@ -295,6 +295,7 @@
      let _actions = []      // 工具栏按钮
      let _operations = []   // 操作列按钮(存在时)
      let colors = { primary: '#1890ff', yellow: '#c49f47', orange: 'orange', danger: '#ff4d4f', green: '#26C281', dgreen: '#32c5d2', purple: '#8E44AD', cyan: '#13c2c2', gray: '#E7E7EF', default: 'rgba(0, 0, 0, 0.65)' }
      config.action.forEach(item => {
        item.logLabel = MenuName + '-' + item.label // 用于sPC_TableData_InUpDe记录操作按钮
@@ -345,11 +346,26 @@
            item.controlVals = [(item.controlVal || '')]
          }
        }
        item.show = 'button'
        let _c = item.class ? item.class.replace('border-', '') : ''
        let color = colors[_c] || '#1890ff'
        if (item.position === 'toolbar') {
          item.$toolbtn = true
          if (item.class === 'default') {
            item.style = {color: 'rgba(0, 0, 0, 0.65)', backgroundColor: '#fff', borderColor: '#d9d9d9', marginRight: '15px'}
          } else if (item.class.indexOf('border') > -1) {
            item.style = {color: color, backgroundColor: '#fff', borderColor: color, marginRight: '15px'}
          } else {
            item.style = {color: item.class === 'gray' ? 'rgba(0, 0, 0, 0.65)' : '#fff', backgroundColor: color, borderColor: color, marginRight: '15px'}
          }
          _actions.push(item)
        } else if (item.position === 'grid') {
          item.style = {color: color, backgroundColor: 'transparent', borderColor: 'transparent'}
          _operations.push(item)
        }
      })
@@ -587,6 +603,8 @@
    let _orderBy = orderBy || setting.order
    let param = UtilsDM.getQueryDataParams(setting, search, _orderBy, pageIndex, pageSize, BID)
    delete param.s_version_up
    let result = await Api.genericInterface(param)
    this.getStatFieldsValue()
@@ -678,6 +696,8 @@
    let _orderBy = orderBy || setting.order
    let param = UtilsDM.getQueryDataParams(setting, search, _orderBy, pageIndex, pageSize, BID, id)
    delete param.s_version_up
    let result = await Api.genericInterface(param)
    if (result.status) {
      let data = fromJS(this.state.data).toJS()
src/tabviews/custom/components/calendar/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification, Modal } from 'antd'
import { Spin } from 'antd'
import Api from '@/api'
import asyncComponent from '@/utils/asyncComponent'
@@ -264,36 +264,13 @@
        loading: false
      })
      
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/card/balcony/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification, Checkbox, Modal } from 'antd'
import { Spin, Checkbox } from 'antd'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
@@ -413,37 +413,14 @@
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/card/cardcellList/index.jsx
@@ -8,6 +8,7 @@
import asyncComponent from '@/utils/asyncComponent'
import { getMark } from '@/utils/utils.js'
import MkIcon from '@/components/mk-icon'
import MKEmitter from '@/utils/events.js'
import Encrypts from '@/components/encrypts'
import './index.scss'
@@ -93,16 +94,45 @@
  openNewView = (e, card) => {
    const { cardCell, data, cards } = this.props
    if (data.$disabled) return
    e.stopPropagation()
    if (card.anchors && card.anchors.length > 0) {
    if (card.anchors) {
      let id = card.anchors[card.anchors.length - 1]
      let node = document.getElementById('anchor' + id)
      node && node.scrollIntoView({behavior: 'smooth', block: 'center', inline: 'nearest'})
    }
    if (!card.link || (card.linkType === 'qywx' || card.linkType === 'linkmenu')) return
    e.stopPropagation()
    if (card.linkType === 'qywx') return
    if (card.linkType === 'linkmenu') {
      if (card.linkThdMenu) {
        let __param = {
          $BID: data.$$uuid
        }
        if (card.field) {
          __param.$searchkey = card.field.toLowerCase()
          __param.$searchval = data[card.field] || ''
        }
        if (card.joint === 'true' && card.linkThdMenu.urlFields) {
          let lower = {}
          Object.keys(data).forEach(key => {
            lower[key.toLowerCase()] = data[key]
          })
          card.linkThdMenu.urlFields.split(',').forEach(field => {
            __param[field] = lower[field.toLowerCase()] || ''
          })
        }
        let tabmenu = card.linkThdMenu
        tabmenu.param = __param
        MKEmitter.emit('modifyTabs', tabmenu, true)
      }
      return
    }
    
    let url = ''
@@ -434,7 +464,7 @@
          }
          if (card.copyable === 'true') {
            if (card.link || (card.anchors && card.anchors.length > 0)) {
            if (card.link || card.anchors) {
              let url = orival
              if (card.link === 'static') {
@@ -474,23 +504,31 @@
          }
          className = mark.signType
        }
        if (card.link || (card.anchors && card.anchors.length > 0)) {
          _style.cursor = 'pointer'
        }
    
        if (card.bgImage && data[card.bgImage]) {
          _style.backgroundImage = `url('${data[card.bgImage]}')`
        }
        contents.push(
          <div className={'ant-col ant-col-' + card.width} key={card.uuid} style={_style_} span={card.width}>
            <div style={_style} onClick={(e) => {this.openNewView(e, card)}}>
              {card.alignItems ? <TextCell card={card} className={'ant-mk-text line' + (card.height || '') + className} value={val}/> :
              <div className={'ant-mk-text line' + (card.height || '') + className} style={{height: card.innerHeight}}>{val}</div>}
        if (!data.$disabled && (card.link || card.anchors)) {
          _style.cursor = 'pointer'
          contents.push(
            <div className={'ant-col ant-col-' + card.width} key={card.uuid} style={_style_} span={card.width}>
              <div style={_style} onClick={(e) => {this.openNewView(e, card)}}>
                {card.alignItems ? <TextCell card={card} className={'ant-mk-text line' + (card.height || '') + className} value={val}/> :
                <div className={'ant-mk-text line' + (card.height || '') + className} style={{height: card.innerHeight}}>{val}</div>}
              </div>
            </div>
          </div>
        )
          )
        } else {
          contents.push(
            <div className={'ant-col ant-col-' + card.width} key={card.uuid} style={_style_} span={card.width}>
              <div style={_style}>
                {card.alignItems ? <TextCell card={card} className={'ant-mk-text line' + (card.height || '') + className} value={val}/> :
                <div className={'ant-mk-text line' + (card.height || '') + className} style={{height: card.innerHeight}}>{val}</div>}
              </div>
            </div>
          )
        }
      } else if (card.eleType === 'number') {
        let val = ''
        let _style = {...card.style}
@@ -688,24 +726,31 @@
        _imagestyle.backgroundPosition = _style.backgroundPosition || 'center'
        _imagestyle.backgroundRepeat = _style.backgroundRepeat || 'no-repeat'
  
        if (card.link) {
          _style.cursor = 'pointer'
        }
        if (_style.position === 'absolute') {
          _style.width = '100%'
        }
        let scale = url && card.scale === 'true'
        let urls = url ? url.split(',').filter(Boolean) : ['']
        urls.forEach((u, i) => {
          contents.push(<div className={'ant-col ant-col-' + card.width} key={card.uuid + i} style={_style_} span={card.width}>
            <div style={_style} onClick={(e) => {this.openNewView(e, card)}}>
              <MkPicture style={_imagestyle} lostTip={card.lostTip !== 'false'} scale={scale} url={u} urls={urls}/>
            </div>
          </div>)
        })
        if (card.link && !data.$disabled) {
          _style.cursor = 'pointer'
          urls.forEach((u, i) => {
            contents.push(<div className={'ant-col ant-col-' + card.width} key={card.uuid + i} style={_style_} span={card.width}>
              <div style={_style} onClick={(e) => {this.openNewView(e, card)}}>
                <MkPicture style={_imagestyle} lostTip={card.lostTip !== 'false'} scale={scale} url={u} urls={urls}/>
              </div>
            </div>)
          })
        } else {
          urls.forEach((u, i) => {
            contents.push(<div className={'ant-col ant-col-' + card.width} key={card.uuid + i} style={_style_} span={card.width}>
              <div style={_style}>
                <MkPicture style={_imagestyle} lostTip={card.lostTip !== 'false'} scale={scale} url={u} urls={urls}/>
              </div>
            </div>)
          })
        }
      } else if (card.eleType === 'splitline') {
        let _borderWidth = card.borderWidth === undefined ? 1 : card.borderWidth
        _style_ = _style_ || {}
@@ -822,14 +867,64 @@
            _data = [data]
          }
          let _val = card.formula
          if (/@username@|@fullName@|@bid@/ig.test(_val)) {
            _val = _val.replace(/@username@/ig, sessionStorage.getItem('User_Name') || '').replace(/@fullName@/ig, sessionStorage.getItem('Full_Name') || '').replace(/@bid@/ig, data.$$BID || '')
          }
          try {
            // eslint-disable-next-line
            let func = new Function('data', card.formula)
            let func = new Function('data', _val)
            val = func(_data)
          } catch (e) {
            console.warn(e)
            val = ''
          }
          if (!val && card.noValue === 'hide') { // 空值隐藏
            return null
          } else if (typeof(val) === 'object' && val.type === 'linkmenu') {
            // type: 'linkmenu', linkThdMenu: null, menuId: '', value: ``, defaultValue: '', onclick: 'inner'
            let item = {linkType: 'linkmenu', linkThdMenu: val.linkThdMenu}
            let _val_ = val.value || ''
            if (!item.linkThdMenu && val.menuId) {
              item.linkThdMenu = window.GLOB.mkThdMenus.get(val.menuId) || ''
            }
            if (!item.linkThdMenu && val.defaultValue) {
              _val_ = val.defaultValue
            }
            if (val.onclick === 'inner') {
              contents.push(
                <div className={'ant-col ant-col-' + card.width} key={card.uuid} style={_style_} span={card.width}>
                  <div style={_style}>
                    <div className={'ant-mk-text line' + (card.height || '')} style={{height: card.innerHeight}}>
                      <span onClick={(e) => {this.openNewView(e, item)}} dangerouslySetInnerHTML={{__html: _val_}}></span>
                    </div>
                  </div>
                </div>
              )
            } else {
              _style.cursor = 'pointer'
              contents.push(
                <div className={'ant-col ant-col-' + card.width} key={card.uuid} style={_style_} span={card.width}>
                  <div style={_style} onClick={(e) => {this.openNewView(e, item)}}>
                    <div className={'ant-mk-text line' + (card.height || '')} style={{height: card.innerHeight}} dangerouslySetInnerHTML={{__html: _val_}}></div>
                  </div>
                </div>
              )
            }
          } else {
            contents.push(
              <div className={'ant-col ant-col-' + card.width} key={card.uuid} style={_style_} span={card.width}>
                <div style={_style}>
                  <div className={'ant-mk-text line' + (card.height || '')} style={{height: card.innerHeight}} dangerouslySetInnerHTML={{__html: val}}></div>
                </div>
              </div>
            )
          }
          return
        } else if (card.$sync) {
          if (card.eval === 'false') {
            val = ''
@@ -859,6 +954,9 @@
          val = ''
        } else if (data) {
          let _val = card.formula
          if (/@username@|@fullName@|@bid@/ig.test(_val)) {
            _val = _val.replace(/@username@/ig, sessionStorage.getItem('User_Name') || '').replace(/@fullName@/ig, sessionStorage.getItem('Full_Name') || '').replace(/@bid@/ig, data.$$BID || '')
          }
          Object.keys(data).forEach(key => {
            let reg = new RegExp('@' + key + '@', 'ig')
            _val = _val.replace(reg, data[key])
@@ -880,15 +978,6 @@
        if (!val && card.noValue === 'hide') { // 空值隐藏
          return null
        } else if (card.eval === 'func') {
          contents.push(
            <div className={'ant-col ant-col-' + card.width} key={card.uuid} style={_style_} span={card.width}>
              <div style={_style}>
                <div className={'ant-mk-text line' + (card.height || '')} style={{height: card.innerHeight}} dangerouslySetInnerHTML={{__html: val}}></div>
              </div>
            </div>
          )
          return
        }
        if (card.round && typeof(val) === 'number') {
@@ -947,6 +1036,56 @@
            </div>
          </div>
        )
      } else if (card.eleType === 'tag') {
        let vals = ''
        if (card.datatype === 'static') {
          vals = card.value
        } else {
          vals = data[card.field] || ''
        }
        if (!vals && card.noValue === 'hide') { // 空值隐藏
          return null
        }
        vals = vals.split(',').filter(Boolean)
        if (card.signs) {
          vals = vals.map(val => {
            let sign = card.signs.filter(s => s.value === val)[0]
            let cell = {value: val, style: {...card.style}}
            if (sign) {
              cell.style.backgroundColor = sign.background
              cell.style.color = sign.color
              cell.style.borderColor = sign.border
              // delete cell.style.borderTopColor
              // delete cell.style.borderBottomColor
              // delete cell.style.borderLeftColor
              // delete cell.style.borderRightColor
            }
            return cell
          })
          contents.push(
            <div className={'ant-col ant-col-' + card.width} key={card.uuid} style={_style_} span={card.width}>
              <div className="ant-mk-tag">
                {vals.map((item, index) => <span key={index} className="tag-item" style={item.style}>{item.value}</span>)}
              </div>
            </div>
          )
        } else {
          contents.push(
            <div className={'ant-col ant-col-' + card.width} key={card.uuid} style={_style_} span={card.width}>
              <div className="ant-mk-tag">
                {vals.map((val, index) => <span key={index} className="tag-item" style={card.style}>{val}</span>)}
              </div>
            </div>
          )
        }
      } else if (card.eleType === 'color') {
        let color = ''
  
@@ -1006,6 +1145,10 @@
        } else if (data.$$empty) {
          _data = []
        }
        let name = ''
        if (card.showName) {
          name = data[card.showName] || ' '
        }
        _style_ = _style_ || {}
        if (card.wrapStyle) {
@@ -1017,6 +1160,7 @@
        if (['exec', 'prompt', 'pop', 'form'].includes(card.OpenType)) {
          MkButton = <NormalButton
            btn={card}
            name={name}
            BID={data.$$BID}
            BData={data.$$BData || ''}
            disabled={_disabled}
@@ -1045,6 +1189,7 @@
        } else if (card.OpenType === 'popview') {
          MkButton = <PopupButton
            btn={card}
            name={name}
            BID={data.$$BID}
            BData={data.$$BData || ''}
            disabled={_disabled}
@@ -1054,6 +1199,7 @@
        } else if (card.OpenType === 'tab') {
          MkButton = <TabButton
            btn={card}
            name={name}
            BID={data.$$BID}
            BData={data.$$BData || ''}
            disabled={_disabled}
@@ -1062,6 +1208,7 @@
        } else if (card.OpenType === 'innerpage') {
          MkButton = <NewPageButton
            btn={card}
            name={name}
            BID={data.$$BID}
            BData={data.$$BData || ''}
            disabled={_disabled}
src/tabviews/custom/components/card/cardcellList/index.scss
@@ -3,6 +3,10 @@
  position: relative;
  line-height: 1.5;
  
  .ant-col-0:not(.mk-cell-btn) {
    display: block;
    float: left;
  }
  .ant-btn {
    padding: 0;
  }
@@ -17,6 +21,7 @@
    font-style: inherit;
    font-weight: inherit;
    text-decoration: inherit;
    font-family: inherit;
    color: inherit;
    .anticon-copy {
      color: var(--mk-sys-color);
@@ -26,6 +31,7 @@
    font-style: inherit;
    font-weight: inherit;
    text-decoration: inherit;
    font-family: inherit;
    .sequence-wrap {
      display: inline-block;
      width: 21px;
@@ -41,6 +47,7 @@
      font-style: inherit;
      font-weight: inherit;
      text-decoration: inherit;
      font-family: inherit;
    }
  }
  .ant-mk-text:not(.line1):not(.line) {
@@ -88,6 +95,7 @@
    background-size: cover;
  }
  .mk-cell-btn {
    text-align: center;
    min-height: 0px;
    > div {width: 100%;max-width: 100%;}
    button:not(.ant-switch) {
@@ -97,6 +105,7 @@
      min-height: 28px;
      padding: 0;
      overflow: hidden;
      text-align: inherit;
    }
    .ant-checkbox-wrapper:not(.square) {
      .ant-checkbox-inner, .ant-checkbox-checked::after {
@@ -154,6 +163,12 @@
    border-left: 0;
    border-right: 0;
  }
  .ant-mk-tag {
    .tag-item {
      display: inline-block;
      vertical-align: top;
    }
  }
  .ant-mk-check {
    white-space: nowrap;
    overflow: hidden;
src/tabviews/custom/components/card/data-card/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, Empty, notification, message, Row, Col, Pagination, Modal, Switch } from 'antd'
import { Spin, Empty, message, Row, Col, Pagination, Switch } from 'antd'
import { DownOutlined } from '@ant-design/icons'
import Api from '@/api'
@@ -42,12 +42,11 @@
    precards: [],
    nextcards: [],
    selected: 'false',
    supNodes: [],
    supComs: null,
    pickup: false
  }
  loaded = false
  supModules = []
  UNSAFE_componentWillMount () {
    const { config } = this.props
@@ -124,17 +123,12 @@
      _config.wrap.selected = 'false'
    }
    let supComs = null
    if (_config.wrap.supType === 'multi') {
      supComs = _config.supNodes.map(item => item.componentId)
    }
    _config.wrap.selStyle = _config.wrap.selStyle || 'active'
    _config.wrap.pagestyle = _config.wrap.pagestyle || 'page'
    _config.wrap.scale = _config.wrap.scale === 'true' ? 'scale' : ''
    _config.wrap.layout = (_config.wrap.layout || 'grid') + '-layout float-' + (_config.wrap.cardFloat || 'left')
    _config.wrap.wrapClass = `${_config.wrap.selStyle} ${_config.wrap.cardType || ''} ${_config.wrap.scale}`
    _config.wrap.wrapClass = `${_config.wrap.selStyle} ${_config.wrap.cardType || ''} ${_config.wrap.scale} ${config.wrap.hover === 'true' ? 'mk-hover' : ''}`
    if (_config.wrap.shifting === 'true') {
      _config.wrap.shifting = 'shifting'
@@ -197,7 +191,6 @@
    this.setState({
      pageSize: _config.setting.pageSize || 10,
      pageOptions,
      supComs,
      selected,
      precards,
      nextcards,
@@ -412,14 +405,14 @@
   * @param {*} btn        // 执行的按钮
   */
  refreshByButtonResult = (menuId, position, btn, id = '', lines) => {
    const { config, BID, supComs, supNodes } = this.state
    const { config, BID } = this.state
    if (config.uuid !== menuId) return
    if (supComs) {
      if (position === 'mainline' || position === 'popclose') { // 主表刷新,去除同步刷新组件
        let supNode = supNodes[supNodes.length - 1]
        supComs.forEach((item, i) => {
    if (config.supNodes) {
      if (position === 'mainline' || position === 'popclose') {
        let supNode = this.supModules[this.supModules.length - 1]
        config.supNodes.forEach((item, i) => {
          setTimeout(() => {
            if (supNode && supNode.key === item) {
              MKEmitter.emit('reloadData', item, supNode.value)
@@ -678,28 +671,26 @@
  }
  resetParentParam = (MenuID, id, data) => {
    const { config, supComs } = this.state
    const { config } = this.state
    if (supComs) {
      if (!supComs.includes(MenuID)) return
      let supNodes = this.state.supNodes.filter(item => item.key !== MenuID)
    if (config.supNodes) {
      if (!config.supNodes.includes(MenuID)) return
      this.supModules = this.supModules.filter(item => item.key !== MenuID)
      let bid = ''
      let _data = null
      if (id) {
        supNodes.push({key: MenuID, value: id, data})
        this.supModules.push({key: MenuID, value: id, data})
      }
      if (supNodes.length > 0) {
        bid = supNodes[supNodes.length - 1].value
        _data = supNodes[supNodes.length - 1].data
      if (this.supModules.length > 0) {
        bid = this.supModules[this.supModules.length - 1].value
        _data = this.supModules[this.supModules.length - 1].data
      }
      if (bid !== this.state.BID || bid !== '') {
        this.setState({ BID: bid, BData: _data, pageIndex: 1, supNodes }, () => {
        this.setState({ BID: bid, BData: _data, pageIndex: 1 }, () => {
          this.loadData()
        })
      } else {
        this.setState({ supNodes })
      }
    } else {
      if (!config.setting.supModule || config.setting.supModule !== MenuID) return
@@ -755,13 +746,9 @@
        loading: false
      })
      
      if (selected !== 'false' || (id && config.wrap.selected !== 'false')) {
        this.prevCheck(id)
      } else {
        MKEmitter.emit('resetSelectLine', config.uuid, '', '')
        if (config.setting.$hasSyncModule) {
          MKEmitter.emit('syncBalconyData', config.uuid, [], false)
        }
      MKEmitter.emit('resetSelectLine', config.uuid, '', '')
      if (config.setting.$hasSyncModule) {
        MKEmitter.emit('syncBalconyData', config.uuid, [], false)
      }
      return
    }
@@ -871,37 +858,15 @@
          this.timer && this.timer.stop()
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
@@ -1043,11 +1008,8 @@
      this.setState({
        loading: false
      })
      notification.error({
        top: 92,
        message: result.message,
        duration: 10
      })
      UtilsDM.queryFail(result)
    }
  }
@@ -1251,7 +1213,7 @@
              let className = 'mk-card '
              if (config.wrap.parity === 'true') {
                if (index % 2 === 1) {
                  className += 'mk-parity-bg '
                  className += 'mk-even-line '
                }
              }
              if (item.$disabled) {
src/tabviews/custom/components/card/data-card/index.scss
@@ -38,8 +38,15 @@
        }
      }
    }
    .mk-parity-bg {
    .mk-even-line {
      .card-item-box {
        background-color: #fafafa;
      }
    }
  }
  .data-zoom.mk-hover {
    .mk-card {
      .card-item-box:hover {
        background-color: var(--mk-sys-color1);
      }
    }
src/tabviews/custom/components/card/double-data-card/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, Empty, notification, Row, Col, Pagination, Modal, Switch } from 'antd'
import { Spin, Empty, Row, Col, Pagination, Switch } from 'antd'
import { DownOutlined, UpOutlined, PlusSquareOutlined, MinusSquareOutlined } from '@ant-design/icons'
import Api from '@/api'
@@ -595,17 +595,8 @@
        total: 0,
        loading: false
      })
      if (selected !== 'false' || (id && config.wrap.selected !== 'false')) {
        setTimeout(() => {
          this.checkTopLine(id)
        }, 10)
        if (selected === 'init') {
          this.setState({selected: 'false'})
        }
      } else {
        MKEmitter.emit('resetSelectLine', config.uuid, '', '')
      }
      MKEmitter.emit('resetSelectLine', config.uuid, '', '')
      return
    }
@@ -771,37 +762,15 @@
          this.timer && this.timer.stop()
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
@@ -959,11 +928,8 @@
      this.setState({
        loading: false
      })
      notification.error({
        top: 92,
        message: result.message,
        duration: 10
      })
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/card/double-data-card/index.scss
@@ -194,14 +194,14 @@
    >.card-row-list {
      >.ant-col:not(.extend-card) {
        .card-item-box:hover {
          background-color: var(--mk-sys-color2);
          background-color: var(--mk-sys-color1);
        }
      }
    }
    .sub-card-wrap.mk-parity-bg {
      .ant-col:nth-child(even){
        .card-item-box:not(:hover) {
          background-color: var(--mk-sys-color1);
          background-color: #fafafa;
        }
      }
    }
@@ -270,7 +270,7 @@
    .sub-card-wrap.mk-parity-bg {
      .ant-col:nth-child(even){
        .card-item-box {
          background-color: var(--mk-sys-color1);
          background-color: #fafafa;
        }
      }
    }
src/tabviews/custom/components/card/prop-card/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification, Col, Row, Modal } from 'antd'
import { Spin, Col, Row } from 'antd'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
@@ -76,6 +76,14 @@
    _data.$$BID = BID || ''
    _data.$$BData = BData || ''
    if (_config.wrap.datatype === 'static' && BData) {
      Object.keys(BData).forEach(key => {
        if (/\$/.test(key)) return
        _data[key] = BData[key]
      })
    }
    if (_config.setting.primaryKey) {
      _data.$$uuid = _data[_config.setting.primaryKey] || ''
    }
@@ -494,8 +502,16 @@
    }
    
    if (config.wrap.datatype === 'static') {
      let _data = {$$BID: BID || '', $$BData: BData, $$empty: true, $$time: new Date().getTime()}
      if (BData) {
        Object.keys(BData).forEach(key => {
          if (/\$/.test(key)) return
          _data[key] = BData[key]
        })
      }
      this.setState({
        data: {$$BID: BID || '', $$BData: BData, $$empty: true, $$time: new Date().getTime()},
        data: _data,
      })
      if (!btn) {
@@ -573,37 +589,15 @@
          this.timer && this.timer.stop()
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/card/table-card/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification, Row, Col, Empty, Pagination, Modal } from 'antd'
import { Spin, Row, Col, Empty, Pagination } from 'antd'
import { DownOutlined } from '@ant-design/icons'
import Api from '@/api'
@@ -12,6 +12,7 @@
import './index.scss'
const CardCellComponent = asyncComponent(() => import('../cardcellList'))
const MainAction = asyncComponent(() => import('@/tabviews/zshare/actionList'))
const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
class TableCard extends Component {
@@ -20,13 +21,14 @@
  }
  state = {
    BID: '',                   // 上级ID
    config: null,              // 图表配置信息
    loading: false,            // 数据加载状态
    search: null,              // 搜索条件
    pageIndex: 1,              // 页码
    total: 0,                  // 总数
    data: null,                // 数据
    BID: '',
    config: null,
    loading: false,
    search: null,
    pageIndex: 1,
    total: 0,
    data: null,
    precards: [],
    BData: ''
  }
@@ -94,12 +96,22 @@
      }
    }
    let precards = []
    _config.subcards = _config.subcards.filter(item => {
      if (item.setting.condition === 'title') {
        precards.push(item)
        return false
      }
      return true
    })
    this.setState({
      BID: BID || '',
      BData: BData || '',
      data: _data,
      config: _config,
      search: _config.$searches
      search: _config.$searches,
      precards
    })
  }
@@ -417,37 +429,15 @@
          this.timer && this.timer.stop()
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
@@ -507,13 +497,14 @@
    }
  }
  getLines = (data) => {
  getLines = (data, lindex) => {
    const { config } = this.state
    let line = []
    config.subcards.forEach((item, index) => {
      let display = item.setting.condition !== 'true'
      let className = ''
      if (!display && item.setting.controlField) {
        let val = data[item.setting.controlField]
@@ -531,12 +522,20 @@
        } else if (item.setting.controlType === '<' && val < item.setting.controlValue) {
          display = true
        }
        className = 'mk_line_' + val
      }
      if (!display) return
      if (config.wrap.parity === 'true') {
        if (lindex % 2 === 1) {
          className += ' mk-even-line'
        }
      }
      line.push(
        <Col key={index} span={24}>
        <Col key={index} className={className} span={24}>
          <div className="card-item-box" style={item.style} onClick={() => {this.openView(item, data)}}>
            <CardCellComponent data={data} cards={config} cardCell={item} elements={item.elements}/>
          </div>
@@ -571,7 +570,7 @@
  }
  render() {
    const { config, loading, data, BID, pageIndex, total } = this.state
    const { config, loading, data, BID, pageIndex, total, BData, precards } = this.state
    if (config.wrap.empty === 'hidden' && (!data || data.length === 0)) return null
    
@@ -584,12 +583,27 @@
          </div> : null
        }
        <NormalHeader config={config} BID={BID} refresh={this.refreshSearch} />
        {data && data.length > 0 ? <Row className={'card-row-list' + (config.wrap.parity === 'true' ? ' mk-parity' : '')} style={{height: config.wrap.contentHeight}}>
          {data.map(item => this.getLines(item))}
        </Row> : null}
        {data && data.length === 0 ? <div className="card-row-list" style={{height: config.wrap.contentHeight}}>
          <Empty description={false}/>
        </div> : null}
        {config.action && config.action.length > 0 ?
          <MainAction
            BID={BID}
            BData={BData}
            setting={config.setting}
            actions={config.action}
            columns={config.columns}
            selectedData={[]}
          /> : null
        }
        <Row className={`card-row-list ${config.wrap.hover === 'true' ? 'mk-hover' : ''}`} style={{height: config.wrap.contentHeight}}>
          {precards.map((item, index) => (
            <Col key={index} className="extend-card" span={24}>
              <div className="card-item-box" style={item.style}>
                <CardCellComponent data={data && data[0] ? data[0] : {}} cards={config} cardCell={item} elements={item.elements}/>
              </div>
            </Col>
          ))}
          {data && data.length > 0 ? data.map((item, index) => this.getLines(item, index)) : null}
          {data && data.length === 0 ? <Empty description={false}/> : null}
        </Row>
        {config.wrap.pagestyle === 'page' ? <Pagination size="small" current={pageIndex} total={total} onChange={this.changePageIndex} /> : null}
        {config.wrap.pagestyle === 'more' && data && data.length > 0 ? <div className={'mk-more' + (config.setting.pageSize * pageIndex >= total ? ' disabled' : '')} onClick={this.loadMore}>查看更多<DownOutlined/></div> : null}
      </div>
src/tabviews/custom/components/card/table-card/index.scss
@@ -33,13 +33,6 @@
    clear: both;
  }
  .mk-parity {
    >.ant-col:nth-child(even) {
      .card-item-box {
        background-color: var(--mk-sys-color1);
      }
    }
  }
  .card-row-list {
    overflow-y: auto;
    .card-item-box {
@@ -47,9 +40,17 @@
      background-color: #ffffff;
      transition: all 0.3s;
    }
    >.active >.card-item-box {
      border-color: #1890ff!important;
      box-shadow: 0 0 3px #1890ff;
    .mk-even-line {
      .card-item-box {
        background-color: #fafafa;
      }
    }
  }
  .card-row-list.mk-hover {
    >.ant-col:not(.extend-card) {
      .card-item-box:hover {
        background-color: var(--mk-sys-color1);
      }
    }
  }
  .card-row-list::-webkit-scrollbar {
src/tabviews/custom/components/carousel/data-card/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, Empty, notification, Carousel, Modal, Checkbox, Button } from 'antd'
import { Spin, Empty, Carousel, Modal, Checkbox, Button } from 'antd'
import { LeftOutlined, RightOutlined } from '@ant-design/icons'
import Api from '@/api'
@@ -379,37 +379,15 @@
          this.timer && this.timer.stop()
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/carousel/prop-card/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification, Carousel, Modal, Checkbox, Button } from 'antd'
import { Spin, Carousel, Modal, Checkbox, Button } from 'antd'
import { LeftOutlined, RightOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -382,37 +382,15 @@
          this.timer && this.timer.stop()
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/chart/antv-G6/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal, notification, Spin, Empty } from 'antd'
import { Spin, Empty } from 'antd'
import G6 from '@antv/g6'
import Api from '@/api'
@@ -1167,36 +1167,14 @@
        this.data = result.data || []
        this.handleData()
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/chart/antv-X6/index.jsx
@@ -1,8 +1,8 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, Tooltip, message, Modal, notification, Switch } from 'antd'
import { UndoOutlined, RedoOutlined, VerticalAlignTopOutlined, VerticalAlignBottomOutlined, SaveOutlined, ZoomInOutlined, ZoomOutOutlined, OneToOneOutlined, QuestionCircleOutlined, ClearOutlined } from '@ant-design/icons'
import { Spin, Tooltip, message, Modal, notification, Switch, Button } from 'antd'
import { UndoOutlined, RedoOutlined, VerticalAlignTopOutlined, VerticalAlignBottomOutlined, ZoomInOutlined, ZoomOutOutlined, OneToOneOutlined, QuestionCircleOutlined, ClearOutlined } from '@ant-design/icons'
import { Graph, Shape } from '@antv/x6'
import { Stencil } from '@antv/x6-plugin-stencil'
import { Transform } from '@antv/x6-plugin-transform'
@@ -388,7 +388,8 @@
    status: 0,
    flowname: '',
    flowcode: '',
    orgs: []
    orgs: [],
    empty: true
  }
  selectNode = null
@@ -526,6 +527,7 @@
                    worker_id: work.worker_id || '',
                    workercode: work.workercode || '',
                    workername: work.workername || '',
                    job_type: job.job_type || '',
                    parentIds: [com.OrgCode, dep.co_pro_code, job.jobcode],
                    parentNames: [com.OrgName, dep.co_pro_name, job.jobname]
                  }
@@ -553,6 +555,7 @@
                    worker_id: work.worker_id || '',
                    workercode: work.workercode || '',
                    workername: work.workername || '',
                    job_type: job.job_type || '',
                    parentIds: [com.OrgCode, dep.co_pro_code, job.jobcode, group.work_group],
                    parentNames: [com.OrgName, dep.co_pro_name, job.jobname, group.work_group]
                  }
@@ -590,8 +593,8 @@
    const { BID } = this.state
    if (!BID) {
      this.cells = []
      if (!is(fromJS(this.data), fromJS([]))) {
        this.cells = []
        this.handleData()
      }
      this.setState({empty: true})
@@ -624,7 +627,8 @@
        status: item.status || 0,
        flowname: item.works_flow_name || '',
        flowcode: item.works_flow_code || '',
        loading: false
        loading: false,
        empty: cells.length === 0
      })
      if (result.message) {
@@ -994,6 +998,18 @@
      graph.clearTransformWidgets()
    })
    graph.on('node:dblclick', () => {
      setTimeout(() => {
        MKEmitter.emit('mk-x6-dbclick')
      }, 100)
    })
    graph.on('edge:dblclick', () => {
      setTimeout(() => {
        MKEmitter.emit('mk-x6-dbclick')
      }, 100)
    })
    graph.on('blank:click', () => {
      this.selectNode = null
      
@@ -1003,8 +1019,19 @@
      if (!isNew) return
      let target = edge.getTargetCell()
      let source = edge.getSourceCell()
      if (!target) return
      if (!target || !source) return
      if (source.prop('mknode') === 'end') {
        notification.warning({
          top: 92,
          message: '结束节点不可添加分支!',
          duration: 2
        })
        graph.removeCells([edge])
        return
      }
      let mkdata = target.prop('mkdata')
@@ -1012,6 +1039,8 @@
        edge.prop('mknode', 'endEdge')
      } else if (target.prop('mknode') === 'start') {
        edge.prop('mknode', 'startEdge')
      } else if (source.prop('mknode') === 'start') {
        edge.prop('mknode', 'firstEdge')
      }
      if (mkdata) {
        edge.prop('mkdata', {status: mkdata.status, statusName: mkdata.statusName})
@@ -1544,6 +1573,26 @@
    this.mkGraph.zoomTo(1)
  }
  close = () => {
    const { config } = this.state
    let nodes = this.mkGraph.toJSON()
    if (!is(fromJS(nodes.cells), fromJS(this.cells))) {
      confirm({
        title: '流程图已修改,确定关闭吗?',
        okText: '确定',
        cancelText: '取消',
        onOk() {
          MKEmitter.emit('closeTabView', config.$pageId)
        },
        onCancel() {}
      })
    } else {
      MKEmitter.emit('closeTabView', config.$pageId)
    }
  }
  save = () => {
    const { BID, plot, status, flowname, flowcode } = this.state
@@ -1596,7 +1645,7 @@
        }
      })
      if (start_num !== 1 || end_num !== 1 || unvalid) {
      if (start_num !== 1 || end_num === 0 || unvalid) {
        _status = 0
      }
    }
@@ -1638,6 +1687,7 @@
                  loading: false,
                  status: _status
                })
                this.cells = nodes.cells
              } else {
                notification.error({
                  top: 92,
@@ -1659,6 +1709,7 @@
              loading: false,
              status: _status
            })
            this.cells = nodes.cells
          }
        } else {
          notification.error({
@@ -2080,12 +2131,12 @@
          message: '请添加结束节点!',
          duration: 2
        })
      } else if (end_num > 1) {
        notification.warning({
          top: 92,
          message: '结束节点不可添加多个!',
          duration: 2
        })
      // } else if (end_num > 1) {
      //   notification.warning({
      //     top: 92,
      //     message: '结束节点不可添加多个!',
      //     duration: 2
      //   })
      } else if (emptyNode) {
        let errmsg = '部分节点未设置基本信息。'
        if (emptyNode.attrs && emptyNode.attrs.text && emptyNode.attrs.text.text) {
@@ -2202,12 +2253,12 @@
  }
  render() {
    const { loading, config, node, orgs, status, flowname } = this.state
    const { loading, config, node, orgs, status, flowname, empty } = this.state
    let style = {...config.style}
    if (config.plot.function === 'show') {
      if (config.plot.empty === 'hidden' && this.cells.length === 0) {
      if (config.plot.empty === 'hidden' && empty) {
        style.position = 'absolute'
        style.width = '100%'
        style.zIndex = -1
@@ -2264,18 +2315,15 @@
            <Tooltip title="清空">
              <ClearOutlined onClick={this.clearNode}/>
            </Tooltip>
            <Tooltip overlayStyle={{maxWidth: 260}} title="快捷键:复制(ctrl+c)、剪切(ctrl+x)、粘贴(ctrl+v)、后退(ctrl+z)、前进(ctrl+shift+z)、删除(backspace 或 delete)">
            <Tooltip overlayStyle={{maxWidth: 310}} title="快捷键:复制(ctrl+c)、剪切(ctrl+x)、粘贴(ctrl+v)、后退(ctrl+z)、前进(ctrl+shift+z)、删除(backspace 或 delete);双击节点或连线可编辑自定义信息。">
              <QuestionCircleOutlined />
            </Tooltip>
          </div>
          <div className="flow-name">{flowname}</div>
          <div className="right-tool">
            <Tooltip title="启用/停用">
              <Switch size="small" style={{marginRight: '10px'}} checked={status === 10} onChange={this.changeStatus} />
            </Tooltip>
            <Tooltip title="保存">
              <SaveOutlined style={{marginRight: '10px'}} onClick={this.save}/>
            </Tooltip>
            <Switch size="large" checked={status === 10} checkedChildren="启" unCheckedChildren="停" onChange={this.changeStatus} />
            <Button className="save" onClick={this.save}>保存</Button>
            <Button className="close" onClick={this.close}>关闭</Button>
          </div>
        </div>
        <div className="canvas" style={{width: '100%', minHeight: config.plot.height, height: config.plot.height}} id={config.uuid + 'canvas'}>
src/tabviews/custom/components/chart/antv-X6/index.scss
@@ -32,10 +32,34 @@
    .right-tool {
      float: right;
      line-height: 40px;
      .anticon {
        margin: 0 10px;
        font-size: 16px;
        cursor: pointer;
      .ant-switch {
        min-width: 60px;
        height: 28px;
        line-height: 28px;
        margin-top: -2px;
        .ant-switch-inner {
          font-size: 14px;
        }
      }
      .ant-switch:after {
        width: 24px;
        height: 24px;
      }
      .save, .save:hover, .save:active, .save:focus {
        border-color: var(--mk-sys-color);
        background-color: var(--mk-sys-color);
        color: #ffffff;
        margin: 0 15px;
        height: 30px;
      }
      .close {
        margin-right: 15px;
      }
      .close:hover, .close:active, .close:focus {
        border-color: var(--mk-sys-color);
        color: var(--mk-sys-color);
        height: 30px;
      }
    }
  }
src/tabviews/custom/components/chart/antv-X6/nodeupdate/index.jsx
@@ -6,6 +6,7 @@
import ColorSketch from '@/tabviews/zshare/mutilform/mkColor'
import NodeForm from './nodeform'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const { TextArea } = Input
@@ -30,6 +31,10 @@
    })
  }
  componentDidMount () {
    MKEmitter.addListener('mk-x6-dbclick', this.trigger)
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (!is(fromJS(this.props.node), fromJS(nextProps.node))) {
      this.setState({
@@ -42,6 +47,25 @@
        })
      })
    }
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('mk-x6-dbclick', this.trigger)
  }
  trigger = () => {
    const { formlist } = this.state
    if (!formlist || formlist.findIndex(item => item.type === 'other') === -1) return
    this.setState({visible: true})
  }
  getFormList = (node) => {
@@ -496,10 +520,12 @@
        </Form>
        <Modal
          title={mknode && mknode.shape === 'edge' ? '连线编辑' : '节点编辑'}
          wrapClassName="mk-x6-modal"
          visible={visible}
          closable={false}
          maskClosable={false}
          width={1050}
          centered={true}
          onOk={this.confirm}
          onCancel={() => this.setState({visible: false})}
          destroyOnClose
src/tabviews/custom/components/chart/antv-X6/nodeupdate/index.scss
@@ -110,6 +110,7 @@
}
.normal-node-form {
  padding: 0 24px;
  >.ant-row >.ant-col-24 {
    .ant-form-item-label {
      width: 16%;
@@ -117,6 +118,12 @@
    .ant-form-item-control-wrapper {
      width: 84%;
    }
  }
  .mk-split {
    color: var(--mk-sys-color);
    border-bottom: 1px solid #e8e8e8;
    padding-left: 15px;
    padding-bottom: 5px;
  }
  .member-input {
    display: inline-block;
@@ -179,7 +186,26 @@
    }
  }
}
.mk-x6-modal {
  .ant-modal-body {
    max-height: calc(100vh - 170px);
    overflow-y: auto;
  }
  .ant-modal-body::-webkit-scrollbar {
    width: 7px;
  }
  .ant-modal-body::-webkit-scrollbar-thumb {
    border-radius: 5px;
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
    background: rgba(0, 0, 0, 0.13);
  }
  .ant-modal-body::-webkit-scrollbar-track {
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
    border-radius: 3px;
    border: 1px solid rgba(0, 0, 0, 0.07);
    background: rgba(0, 0, 0, 0);
  }
}
.member-modal {
  .ant-modal {
    top: 50px;
src/tabviews/custom/components/chart/antv-X6/nodeupdate/nodeform.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, Tooltip, InputNumber, Switch } from 'antd'
import { Form, Row, Col, Input, Radio, Tooltip, InputNumber, Switch, notification } from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
import MemberForm from './memberform'
@@ -10,18 +10,62 @@
  static propTpyes = {
    node: PropTypes.any,
    data: PropTypes.any,
    orgs: PropTypes.array
    orgs: PropTypes.array,
    handleSubmit: PropTypes.func
  }
  state = {
    flowType: this.props.data.flowType || 'approval',
    execCondition: this.props.data.execCondition === 'open',
    flowType: 'approval',
    execCondition: false,
    approvalMethod: 'orsign',
    readOnly: false,
    options: []
  }
  UNSAFE_componentWillMount() {
    const { node, data } = this.props
    let options = []
    let readOnly = false
    if (node.mknode === 'start') {
      readOnly = true
    } else if (node.mknode === 'end') {
    } else if (node.mknode === 'endEdge') {
      options = ['approvalMethod']
    } else if (node.mknode === 'startEdge') {
      readOnly = true
    } else if (node.mknode === 'firstEdge') {
      options = ['approver', 'members', 'copys']
    } else if (node.shape !== 'edge') { // node
      options = ['sign']
    } else {
      options = ['flowType', 'approvalMethod', 'approver', 'members', 'copys', 'execCondition']
    }
    this.setState({
      flowType: data.flowType || 'approval',
      execCondition: options.includes('execCondition') && data.execCondition === 'open',
      approvalMethod: data.approvalMethod || 'orsign',
      options,
      readOnly
    })
  }
  handleConfirm = () => {
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          if (values.approvalMethod === 'countersign' && values.members.length > 5) {
            notification.warning({
              top: 92,
              message: '会签时审批人不可超过5人!',
              duration: 10
            })
            return
          }
          if (values.execCondition === true) {
            values.execCondition = 'open'
          } else if (values.execCondition === false) {
@@ -40,9 +84,9 @@
  }
  render() {
    const { node, orgs } = this.props
    const { orgs } = this.props
    const { getFieldDecorator } = this.props.form
    const { flowType, execCondition } = this.state
    const { flowType, execCondition, approvalMethod, readOnly, options } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -54,32 +98,22 @@
      }
    }
    let data = this.props.data || {}
    let nodetype = node.shape !== 'edge' ? 'node' : 'edge'
    if (node.mknode === 'start') {
      nodetype = 'start'
    } else if (node.mknode === 'end') {
      nodetype = 'end'
    } else if (node.mknode === 'endEdge') {
      nodetype = 'endEdge'
    } else if (node.mknode === 'startEdge') {
      nodetype = 'startEdge'
    }
    return (
      <Form {...formItemLayout} className="normal-node-form">
        <Row gutter={24}>
          {options.includes('approver') ? <Col span={24}>
            <p className="mk-split">按钮执行命令</p>
          </Col> : null}
          <Col span={12}>
            <Form.Item label="状态值">
              {getFieldDecorator('status', {
                initialValue: data.status,
                rules: [
                  {
                    required: true,
                    message: '请输入状态值!'
                  }
                  { required: true, message: '请输入状态值!' }
                ]
              })(
                <InputNumber readOnly={nodetype !== 'node' && nodetype !== 'edge'} precision={0}/>
                <InputNumber readOnly={readOnly} precision={0} onPressEnter={() => this.props.handleSubmit()}/>
              )}
            </Form.Item>
          </Col>
@@ -88,11 +122,11 @@
              {getFieldDecorator('statusName', {
                initialValue: data.statusName || ''
              })(
                <Input placeholder="" autoComplete="off"/>
                <Input autoComplete="off" onPressEnter={() => this.props.handleSubmit()}/>
              )}
            </Form.Item>
          </Col>
          {nodetype === 'node' ? <Col span={12}>
          {options.includes('sign') ? <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="标记将作为节点ID">
                <QuestionCircleOutlined className="mk-form-tip" />
@@ -102,11 +136,11 @@
              {getFieldDecorator('sign', {
                initialValue: data.sign || ''
              })(
                <Input placeholder="" autoComplete="off"/>
                <Input autoComplete="off" onPressEnter={() => this.props.handleSubmit()}/>
              )}
            </Form.Item>
          </Col> : null}
          {nodetype === 'edge' ? <Col span={12}>
          {options.includes('flowType') ? <Col span={12}>
            <Form.Item label="操作类型">
              {getFieldDecorator('flowType', {
                initialValue: flowType
@@ -118,7 +152,48 @@
              )}
            </Form.Item>
          </Col> : null}
          {nodetype === 'edge' ? <Col span={12}>
          {options.includes('approvalMethod') && flowType !== 'reject' ? <Col span={12}>
            <Form.Item label="审批方式">
              {getFieldDecorator('approvalMethod', {
                initialValue: approvalMethod
              })(
                <Radio.Group onChange={(e) => this.setState({approvalMethod: e.target.value})}>
                  <Radio value="orsign">或签</Radio>
                  <Radio value="countersign">会签</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null}
          {options.includes('approvalMethod') && flowType !== 'reject' && approvalMethod === 'countersign' ? <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="示例:“财务经理张总已审核”,其中“已审核”为会签标记。">
                <QuestionCircleOutlined className="mk-form-tip" />
                会签标记
              </Tooltip>
            }>
              {getFieldDecorator('mark', {
                initialValue: data.mark || '已审核',
                rules: [
                  { required: true, message: '请输入会签标记!' }
                ]
              })(
                <Input autoComplete="off" onPressEnter={() => this.props.handleSubmit()}/>
              )}
            </Form.Item>
          </Col> : null}
          <Col span={24}>
            <Form.Item label="备注">
              {getFieldDecorator('remark', {
                initialValue: data.remark || ''
              })(
                <TextArea rows={2}/>
              )}
            </Form.Item>
          </Col>
          {options.includes('approver') ? <Col span={24}>
            <p className="mk-split">下一步审批权限</p>
          </Col> : null}
          {options.includes('approver') ? <Col span={12}>
            <Form.Item label="设置审批人">
              {getFieldDecorator('approver', {
                initialValue: data.approver || 'member'
@@ -131,32 +206,19 @@
              )}
            </Form.Item>
          </Col> : null}
          {nodetype === 'edge' && flowType !== 'reject' ? <Col span={12}>
            <Form.Item label="审批方式">
              {getFieldDecorator('approvalMethod', {
                initialValue: data.approvalMethod || 'orsign'
              })(
                <Radio.Group>
                  <Radio value="countersign">会签</Radio>
                  <Radio value="orsign">或签</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null}
          {nodetype === 'edge' ? <Col span={12}>
          {options.includes('members') ? <Col span={12}>
            <Form.Item label="审批人">
              {getFieldDecorator('members', {
                initialValue: data.members || [],
                rules: [{
                  required: true,
                  message: '请添加审批人!'
                }]
                rules: [
                  { required: true, message: '请添加审批人!' }
                ]
              })(
                <MemberForm orgs={orgs} title="审批人"/>
              )}
            </Form.Item>
          </Col> : null}
          {nodetype === 'edge' ? <Col span={12}>
          {options.includes('copys') ? <Col span={12}>
            <Form.Item label="抄送人">
              {getFieldDecorator('copys', {
                initialValue: data.copys || []
@@ -165,7 +227,10 @@
              )}
            </Form.Item>
          </Col> : null}
          {nodetype === 'edge' && flowType !== 'reject' ? <Col span={12}>
          {options.includes('execCondition') && flowType !== 'reject' ? <Col span={24}>
            <p className="mk-split">分支执行条件</p>
          </Col> : null}
          {options.includes('execCondition') && flowType !== 'reject' ? <Col span={12}>
            <Form.Item label="执行条件">
              {getFieldDecorator('execCondition', {
                valuePropName: 'checked',
@@ -175,7 +240,7 @@
              )}
            </Form.Item>
          </Col> : null}
          {nodetype === 'edge' && flowType !== 'reject' && execCondition ? <Col span={12}>
          {flowType !== 'reject' && execCondition ? <Col span={12}>
            <Form.Item label="对比方式">
              {getFieldDecorator('match', {
                initialValue: data.match || '='
@@ -191,7 +256,7 @@
              )}
            </Form.Item>
          </Col> : null}
          {nodetype === 'edge' && flowType !== 'reject' && execCondition ? <Col span={12}>
          {flowType !== 'reject' && execCondition ? <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="对比值中不可包含制表符、空格、换行符等。">
                <QuestionCircleOutlined className="mk-form-tip" />
@@ -201,19 +266,10 @@
              {getFieldDecorator('matchVal', {
                initialValue: data.matchVal || ''
              })(
                <Input placeholder="" autoComplete="off"/>
                <Input autoComplete="off" onPressEnter={() => this.props.handleSubmit()}/>
              )}
            </Form.Item>
          </Col> : null}
          <Col span={24}>
            <Form.Item label="备注">
              {getFieldDecorator('remark', {
                initialValue: data.remark || ''
              })(
                <TextArea rows={2}/>
              )}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
src/tabviews/custom/components/chart/antv-bar-line/index.jsx
@@ -3,7 +3,7 @@
import { is, fromJS } from 'immutable'
import { Chart } from '@antv/g2'
import DataSet from '@antv/data-set'
import { Spin, Empty, notification, Modal } from 'antd'
import { Spin, Empty } from 'antd'
import { DownloadOutlined } from '@ant-design/icons'
import moment from 'moment'
@@ -662,37 +662,15 @@
          this.timer && this.timer.stop()
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/chart/antv-dashboard/index.jsx
@@ -2,7 +2,7 @@
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Chart, registerShape } from '@antv/g2'
import { Spin, notification, Modal } from 'antd'
import { Spin } from 'antd'
// import { DownloadOutlined } from '@ant-design/icons'
import moment from 'moment'
@@ -368,37 +368,15 @@
          this.timer && this.timer.stop()
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/chart/antv-pie/index.jsx
@@ -3,7 +3,7 @@
import { is, fromJS } from 'immutable'
import { Chart } from '@antv/g2'
import DataSet, { DataView } from '@antv/data-set'
import { Spin, Empty, notification, Modal } from 'antd'
import { Spin, Empty } from 'antd'
// import { DownloadOutlined } from '@ant-design/icons'
import moment from 'moment'
@@ -316,37 +316,15 @@
          this.timer && this.timer.stop()
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/chart/antv-scatter/index.jsx
@@ -2,7 +2,7 @@
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Chart } from '@antv/g2'
import { Spin, Empty, notification, Modal } from 'antd'
import { Spin, Empty } from 'antd'
import { DownloadOutlined } from '@ant-design/icons'
import moment from 'moment'
@@ -347,37 +347,15 @@
          this.timer && this.timer.stop()
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/chart/custom-chart/index.jsx
@@ -4,7 +4,7 @@
import { Chart } from '@antv/g2'
import DataSet from '@antv/data-set'
import * as echarts from 'echarts'
import { Spin, Empty, notification, Modal } from 'antd'
import { Spin, Empty, notification } from 'antd'
import Api from '@/api'
import Utils from '@/utils/utils.js'
@@ -340,37 +340,15 @@
          this.timer && this.timer.stop()
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/code/sand-box/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification, Modal } from 'antd'
import { Spin, notification } from 'antd'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
@@ -261,36 +261,14 @@
        loading: false
      })
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/editor/braft-editor/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification, Modal } from 'antd'
import { Spin } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import Api from '@/api'
@@ -211,37 +211,14 @@
        data: _data,
        loading: false
      })
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/form/simple-form/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification, Modal, Button } from 'antd'
import { Spin, Button } from 'antd'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
@@ -387,36 +387,13 @@
      MKEmitter.emit('resetSelectLine', config.uuid, _data.$$uuid, _data)
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false,
      })
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/form/step-form/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification, Button, Modal } from 'antd'
import { Spin, Button } from 'antd'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
@@ -405,36 +405,13 @@
      MKEmitter.emit('resetSelectLine', config.uuid, _data.$$uuid, _data)
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false,
      })
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/form/tab-form/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification, Modal } from 'antd'
import { Spin } from 'antd'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
@@ -339,36 +339,13 @@
      MKEmitter.emit('resetSelectLine', config.uuid, _data.$$uuid, _data)
      
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false,
      })
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/group/normal-group/index.jsx
@@ -1,5 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
@@ -8,23 +9,32 @@
class NormalGroup extends Component {
  static propTpyes = {
    config: PropTypes.object,        // 组件配置信息
    config: PropTypes.object
  }
  state = {}
  state = {
    visible: true,
    mergeAble: this.props.config.setting.mergeAble === 'true'
  }
  render() {
    const { config } = this.props
    const { config, style } = this.props
    const { visible, mergeAble } = this.state
    if (config.components.length === 0) return (<div style={config.style}></div>)
    if (config.components.length === 0) return (<div className={'ant-col ant-col-' + config.width} style={style}><div style={config.style}></div></div>)
    
    return (
      <div className={'normal-group-wrap ' + (config.setting.layout || '')} id={'anchor' + config.uuid} style={config.style}>
        {config.setting && config.setting.title ? <div className="group-header" style={config.headerStyle}>
          <span className="title">{config.setting.title}</span>
        </div> : null}
        <TabTransfer config={config}/>
        {/* <Row className="component-wrap">{this.getComponents()}</Row> */}
      <div className={`ant-col ant-col-${config.width} ${mergeAble ? ' mk-merge-able mk-ctrl-' + (config.setting.ctrlNumber || 1) : ''} ${visible ? '' : ' close'}`} style={style}>
        <div className={'normal-group-wrap ' + (config.setting.layout || '')} id={'anchor' + config.uuid} style={config.style}>
          <div className="mk-control">
            <DoubleLeftOutlined onClick={() => this.setState({visible: false})}/>
            <DoubleRightOutlined onClick={() => this.setState({visible: true})}/>
          </div>
          {config.setting && config.setting.title ? <div className="group-header" style={config.headerStyle}>
            <span className="title">{config.setting.title}</span>
          </div> : null}
          <TabTransfer config={config}/>
        </div>
      </div>
    )
  }
src/tabviews/custom/components/group/normal-group/index.scss
@@ -13,11 +13,28 @@
      text-decoration: inherit;
      font-weight: inherit;
      font-style: inherit;
      font-family: inherit;
      float: left;
      line-height: inherit;
      margin-left: 10px;
      position: relative;
      z-index: 1;
    }
  }
  .mk-control {
    display: none;
    position: absolute;
    top: 0px;
    right: 0px;
    z-index: 2;
    background: #ffffff;
    box-shadow: 0px 0px 2px #d8d8d8;
    transition: all 0.2s;
    .anticon {
      padding: 5px;
      cursor: pointer;
    }
  }
}
@@ -37,4 +54,90 @@
      width: 5%;
    }
  }
}
.mk-merge-able {
  transition: all 0.2s;
  .normal-group-wrap {
    position: relative;
    transition: all 0.2s;
  }
  .mk-control {
    display: inline-block;
    .anticon-double-left {
      display: inline-block;
    }
    .anticon-double-right {
      display: none;
    }
  }
}
.mk-merge-able.close {
  width: 25px;
  .normal-group-wrap {
    width: 0px;
    margin: 0px!important;
    border-width: 0px!important;
    box-shadow: none!important;
  }
  .mk-control {
    display: inline-block;
    right: -24px;
    .anticon-double-left {
      display: none;
    }
    .anticon-double-right {
      display: inline-block;
    }
  }
}
.mk-merge-able + .ant-col,
.mk-merge-able + .ant-col + .ant-col,
.mk-merge-able + .ant-col + .ant-col + .ant-col,
.mk-merge-able + .ant-col + .ant-col + .ant-col + .ant-col,
.mk-merge-able + .ant-col + .ant-col + .ant-col + .ant-col + .ant-col {
  transition: all 0.2s;
}
.mk-merge-able.close + .ant-col {
  width: calc(100% - 25px);
}
.mk-merge-able.close.mk-ctrl-2 + .ant-col + .ant-col {
  width: calc(100% - 25px);
}
.mk-merge-able.close.mk-ctrl-3 + .ant-col + .ant-col {
  width: calc(100% - 25px);
}
.mk-merge-able.close.mk-ctrl-3 + .ant-col + .ant-col + .ant-col {
  width: calc(100% - 25px);
}
.mk-merge-able.close.mk-ctrl-4 + .ant-col + .ant-col {
  width: calc(100% - 25px);
}
.mk-merge-able.close.mk-ctrl-4 + .ant-col + .ant-col + .ant-col {
  width: calc(100% - 25px);
}
.mk-merge-able.close.mk-ctrl-4 + .ant-col + .ant-col + .ant-col + .ant-col {
  width: calc(100% - 25px);
}
.mk-merge-able.close.mk-ctrl-5 + .ant-col + .ant-col {
  width: calc(100% - 25px);
}
.mk-merge-able.close.mk-ctrl-5 + .ant-col + .ant-col + .ant-col {
  width: calc(100% - 25px);
}
.mk-merge-able.close.mk-ctrl-5 + .ant-col + .ant-col + .ant-col + .ant-col {
  width: calc(100% - 25px);
}
.mk-merge-able.close.mk-ctrl-5 + .ant-col + .ant-col + .ant-col + .ant-col + .ant-col {
  width: calc(100% - 25px);
}
src/tabviews/custom/components/iframe/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Input, Modal, notification, Empty, Spin } from 'antd'
import { Input, Empty, Spin } from 'antd'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
@@ -212,32 +212,9 @@
        data: _data
      })
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/iframe/index.scss
@@ -22,6 +22,7 @@
      font-weight: inherit;
      font-style: inherit;
      line-height: inherit;
      font-family: inherit;
      margin-left: 10px;
      position: relative;
      z-index: 1;
src/tabviews/custom/components/interfaces/interItem/index.jsx
@@ -1,6 +1,5 @@
import {Component} from 'react'
import PropTypes from 'prop-types'
import { notification, Modal } from 'antd'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
@@ -153,39 +152,16 @@
        MKEmitter.emit('interFinish', config.MenuID, config.uuid)
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.loading = false
      this.timer && this.timer.stop()
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      if (config.setting.loadlevel === 'init') {
        MKEmitter.emit('interFinish', config.MenuID, config.uuid)
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/module/account/index.jsx
@@ -81,6 +81,9 @@
        map.set(item.id, true)
        if (item.selected === 'true' && !activeItem) {
          if (res.invoice_type) {
            item.invoice_type = res.invoice_type.map(cell => ({value: cell.invoice_type_code, label: cell.invoice_type_name}))
          }
          activeItem = item
        }
        if (item.months) {
src/tabviews/custom/components/module/account/index.scss
@@ -8,8 +8,7 @@
  color: #000000;
  .ant-select {
    min-width: 250px;
    max-width: 300px;
    width: 270px;
    color: #000000;
  }
src/tabviews/custom/components/module/invoice/index.jsx
New file
@@ -0,0 +1,757 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Select, Form, Input, Button, Modal, Spin, notification } from 'antd'
import { EllipsisOutlined } from '@ant-design/icons'
import moment from 'moment'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import InvoiceTable from './invoiceTable'
import SubTable from './subTable'
import './index.scss'
class InvoiceModule extends Component {
  static propTpyes = {
    config: PropTypes.object
  }
  state = {
    BID: '',
    ID: Utils.getuuid(),
    io: '',
    invTypes: [
      {value: '1', label: '电子发票(增值税专用发票)'},
      {value: '2', label: '电子发票(普通发票)'},
      {value: '3', label: '增值税纸质专用发票'},
      {value: '4', label: '增值税纸质普通发票'},
      {value: '5', label: '增值税电子普通发票'},
      {value: '6', label: '增值税电子专用发票'},
    ],
    invoice_type: '',
    date: moment().format('YYYY年MM月DD日'),
    from_to_name: '',
    from_to_tax_no: '',
    from_to_addr: '',
    from_to_tel: '',
    from_to_bank_name: '',
    from_to_account_no: '',
    from_to_mob: '',
    from_to_email: '',
    from_to_code: '',
    orgname: '',
    tax_no: '',
    addr: '',
    tel: '',
    bank_name: '',
    account_no: '',
    remark: '',
    payee: '',
    reviewer: '',
    drawer: '',
    details: [],
    oriDetails: [],
    book: null,
    loading: false,
    saveType: '',
    tax_type: '',
    reqfields: [],
    requireds: []
  }
  UNSAFE_componentWillMount () {
    const { config } = this.props
    let _config = fromJS(config).toJS()
    let BID = ''
    let BData = ''
    if (_config.setting.supModule) {
      BData = window.GLOB.CacheData.get(_config.setting.supModule)
    } else {
      BData = window.GLOB.CacheData.get(_config.$pageId)
    }
    if (BData) {
      BID = BData.$BID || ''
    }
    if (_config.wrap.invColor) {
      _config.style['--inv-color'] = _config.wrap.invColor
    }
    _config.buyer = this.formatSetting(_config.buyer, 'buyer')
    _config.detail = this.formatSetting(_config.detail, 'detail')
    let book = null
    let pas = {}
    if (config.wrap.supBook) {
      book = window.GLOB.CacheData.get(config.wrap.supBook) || null
      if (book) {
        pas = this.resetParam(book)
      }
    }
    this.setState({
      BID: BID || '',
      config: _config,
      book,
      ...pas
    })
  }
  componentDidMount () {
    const { config } = this.props
    MKEmitter.addListener('reloadData', this.reloadData)
    MKEmitter.addListener('resetSelectLine', this.resetParentParam)
    if (config.wrap.datatype === 'dynamic' && config.setting.onload === 'true') {
      setTimeout(() => {
        this.loadData()
      }, config.setting.delay || 0)
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('reloadData', this.reloadData)
    MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
  }
  formatSetting = (item, type) => {
    item.setting.arr_field = item.columns.map(col => col.field).join(',')
    item.setting.laypage = item.setting.laypage === 'true'
    if (type === 'buyer') {
      item.columns = item.columns.map(cell => {
        if (['from_to_tel', 'from_to_account_no', 'from_to_code'].includes(cell.field)) {
          cell.Hide = 'true'
        } else if (['from_to_email', 'from_to_mob'].includes(cell.field)) {
          cell.Width = 80
        }
        return cell
      })
    } else {
      item.columns = item.columns.map(cell => {
        if (cell.field === 'general_tax_rate') {
          cell.field = 'tax_rate'
          cell.label = '税率'
        }
        if (['Description', 'id', 'small_tax_rate', 'free_tax_mark', 'vat_special_management'].includes(cell.field)) {
          cell.Hide = 'true'
        } else if (['spec'].includes(cell.field)) {
          cell.Width = 150
        } else if (['unit', 'unitprice', 'tax_rate'].includes(cell.field)) {
          cell.Width = 80
        }
        return cell
      })
    }
    if (item.setting.interType !== 'system') {
      item.setting.dataresource = ''
      return item
    }
    let regs = [
      { reg: /@userName@/ig, value: `'${sessionStorage.getItem('User_Name') || ''}'` },
      { reg: /@fullName@/ig, value: `'${sessionStorage.getItem('Full_Name') || ''}'` }
    ]
    if (window.GLOB.externalDatabase !== null) {
      regs.push({
        reg: /@db@/ig,
        value: window.GLOB.externalDatabase
      })
    }
    let _customScript = ''
    let _tailScript = ''
    item.scripts && item.scripts.forEach(script => {
      if (script.status === 'false') return
      if (script.position !== 'back') {
        _customScript += `
        ${script.sql}
        `
      } else {
        _tailScript += `
        ${script.sql}
        `
      }
    })
    delete item.scripts
    item.setting.execute = item.setting.execute !== 'false'
    if (!item.setting.execute) {
      item.setting.dataresource = ''
    }
    if (/\s/.test(item.setting.dataresource)) {
      item.setting.dataresource = '(' + item.setting.dataresource + ') tb'
    }
    if (sessionStorage.getItem('dataM') === 'true') { // 数据权限
      item.setting.dataresource = item.setting.dataresource.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, '\'Y\'')
      _customScript = _customScript.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, '\'Y\'')
      _tailScript = _tailScript.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, '\'Y\'')
    } else {
      item.setting.dataresource = item.setting.dataresource.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'')
      _customScript = _customScript.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'')
      _tailScript = _tailScript.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'')
    }
    regs.forEach(cell => {
      item.setting.dataresource = item.setting.dataresource.replace(cell.reg, cell.value)
      _customScript = _customScript.replace(cell.reg, cell.value)
      _tailScript = _tailScript.replace(cell.reg, cell.value)
    })
    item.setting.customScript = _customScript // 整理后自定义脚本
    item.setting.tailScript = _tailScript     // 后置自定义脚本
    item.setting.custompage = /@pageSize@|@orderBy@/i.test(item.setting.dataresource + item.setting.customScript)
    return item
  }
  resetParentParam = (MenuID, id, data) => {
    const { config } = this.state
    if (config.wrap.supBook === MenuID) {
      let pas = this.resetParam(data)
      this.setState({ book: data, ...pas }, () => {
        this.loadData()
      })
      return
    }
    if (!config.wrap.supModule || config.wrap.supModule !== MenuID) return
    if (id !== this.state.BID || id !== '') {
      this.setState({ BID: id }, () => {
        this.loadData()
      })
    }
  }
  resetParam = (book) => {
    let invTypes = book.invoice_type || []
    let invoice_type = sessionStorage.getItem('pre_invoice_type') || ''
    if (invoice_type && invTypes.findIndex(item => item.value === invoice_type) === -1) {
      invoice_type = ''
    }
    this.getRequired(invoice_type)
    return {
      invoice_type,
      invTypes: invTypes,
      orgname: book.orgname || '',
      tax_no: book.tax_no || '',
      addr: book.addr || '',
      tel: book.tel || '',
      bank_name: book.bank_name || '',
      account_no: book.account_no || '',
      payee: book.payee || '',
      reviewer: book.reviewer || '',
      drawer: book.drawer || '',
      tax_type: book.tax_type || ''
    }
  }
  reloadData = (menuId) => {
    const { config } = this.state
    if (config.uuid !== menuId) return
    this.loadData()
  }
  async loadData() {
    const { config, BID } = this.state
    if (config.wrap.datatype !== 'dynamic') return
    let param = UtilsDM.getQueryDataParams(config.setting, [], config.setting.order, 1, 1, BID)
    this.setState({
      loading: true
    })
    let result = await Api.genericInterface(param)
    if (result.status) {
      this.setState({
        ID: result.data[0][config.setting.primaryKey] || Utils.getuuid(),
        io: '',
        details: [],
        oriDetails: [],
        loading: false
      })
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      UtilsDM.queryFail(result)
    }
  }
  changeType = (val) => {
    sessionStorage.setItem('pre_invoice_type', val)
    this.setState({invoice_type: val})
    this.getRequired(val)
  }
  getRequired = (invoice_type) => {
    if (!invoice_type) return
    let reqfields = []
    let requireds = []
    let rds = [
      {value: 'from_to_name', label: '购买方名称'},
      {value: 'from_to_tax_no', label: '购买方纳税人识别号'},
      {value: 'from_to_addr', label: '购买方地址'},
      {value: 'from_to_tel', label: '购买方电话'},
      {value: 'from_to_bank_name', label: '购买方开户行'},
      {value: 'from_to_account_no', label: '购买方账号'},
      {value: 'from_to_mob', label: '购买方手机号'},
      {value: 'from_to_email', label: '购买方邮箱'},
      {value: 'orgname', label: '销售方名称'},
      {value: 'tax_no', label: '销售方纳税人识别号'},
      {value: 'addr', label: '销售方地址'},
      {value: 'tel', label: '销售方电话'},
      {value: 'bank_name', label: '销售方开户行'},
      {value: 'account_no', label: '销售方账号'},
    ]
    if (invoice_type === 'e_general') {
      reqfields = ['from_to_name', 'from_to_tax_no', 'orgname', 'tax_no']
      rds.forEach(item => {
        if (reqfields.includes(item.value)) {
          requireds.push(item)
        }
      })
    } else if (invoice_type === 'e_special') {
      reqfields = ['from_to_name', 'from_to_tax_no', 'from_to_addr', 'from_to_tel', 'from_to_bank_name', 'from_to_account_no', 'orgname', 'tax_no', 'addr', 'tel', 'bank_name', 'account_no']
      rds.forEach(item => {
        if (reqfields.includes(item.value)) {
          requireds.push(item)
        }
      })
    }
    this.setState({
      reqfields,
      requireds
    })
  }
  saveBill = () => {
    const { config, book, saveType } = this.state
    if (saveType) return
    setTimeout(() => {
      this.getBillMsg().then(() => {
        let sql = this.getPreSql(config.billSaveBtn)
        let param = {
          func: 'sPC_TableData_InUpDe',
          LText: sql,
          exec_type: window.GLOB.execType || 'y',
          timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
          BID: book.id
        }
        param.secretkey = Utils.encrypt('', param.timestamp)
        param.LText = Utils.formatOptions(param.LText, param.exec_type)
        this.setState({
          saveType: 'bill'
        })
        Api.genericInterface(param).then(res => {
          if (res.status) {
            notification.success({
              top: 92,
              message: '保存成功。',
              duration: 5
            })
          } else {
            notification.warning({
              top: 92,
              message: res.message,
              duration: 5
            })
          }
          this.setState({
            saveType: ''
          })
        })
      }, (error) => {
        notification.warning({
          top: 92,
          message: error,
          duration: 5
        })
        return
      })
    }, 20)
  }
  outBill = () => {
    const { config, book, saveType } = this.state
    if (saveType) return
    setTimeout(() => {
      this.getBillMsg().then(() => {
        let sql = this.getPreSql(config.billOutBtn)
        let param = {
          func: 'sPC_TableData_InUpDe',
          LText: sql,
          key_back_type: 'Y',
          exec_type: window.GLOB.execType || 'y',
          timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
          BID: book.id
        }
        param.secretkey = Utils.encrypt('', param.timestamp)
        param.LText = Utils.formatOptions(param.LText, param.exec_type)
        console.info(sql)
      }, (error) => {
        notification.warning({
          top: 92,
          message: error,
          duration: 5
        })
        return
      })
    })
  }
  getPreSql = (btn) => {
    const { book, ID, io, details, oriDetails, invoice_type, from_to_name, from_to_tax_no, from_to_addr, from_to_tel, from_to_bank_name, from_to_account_no, from_to_mob, from_to_email, from_to_code, orgname, tax_no, addr, tel, bank_name, account_no, remark, reviewer, drawer, payee } = this.state
    let BID = book.id
    let userName = sessionStorage.getItem('User_Name') || ''
    let fullName = sessionStorage.getItem('Full_Name') || ''
    let RoleID = sessionStorage.getItem('role_id') || ''
    let departmentcode = sessionStorage.getItem('departmentcode') || ''
    let organization = sessionStorage.getItem('organization') || ''
    let mk_user_type = sessionStorage.getItem('mk_user_type') || ''
    let nation = sessionStorage.getItem('nation') || ''
    let province = sessionStorage.getItem('province') || ''
    let city = sessionStorage.getItem('city') || ''
    let district = sessionStorage.getItem('district') || ''
    let address = sessionStorage.getItem('address') || ''
    let options = fromJS(oriDetails).toJS()
    let price = 0
    let tax = 0
    let lines = details.map(line => {
      let _sql = `Select '${line.productcode}', '${line.productname}', '${line.spec}', '${line.unit}', ${line.bill_count}, ${line.unitprice}, ${line.amount_line}, '${line.tax_classify_code}', '${line.tax_classify_name}', ${line.tax_rate}, ${line.tax_amount}, '${line.free_tax_mark || ''}', '${line.vat_special_management || ''}', '${line.invoice_lp || ''}', '${line.uuid}'`
      let data_type = 'add'
      price += line.amount_line * 100
      tax += line.tax_amount * 100
      if (options.length) {
        options = options.filter(option => {
          if (option.uuid === line.uuid) {
            data_type = 'upt'
            return false
          }
          return true
        })
      }
      return _sql + `, '${data_type}'`
    })
    let _total = (price - tax) / 100
    price = price / 100
    tax = tax / 100
    if (options.length) {
      options.forEach(line => {
        lines.push(`Select '${line.productcode}', '${line.productname}', '${line.spec}', '${line.unit}', ${line.bill_count}, ${line.unitprice}, ${line.amount_line}, '${line.tax_classify_code}', '${line.tax_classify_name}', ${line.tax_rate}, ${line.tax_amount}, '${line.free_tax_mark || ''}', '${line.vat_special_management || ''}', '${line.invoice_lp || ''}', '${line.uuid}', 'del'`)
      })
    }
    lines = lines.join(' union all ')
    let _script = ''
    btn.scripts.forEach(item => {
      if (item.status === 'false') return
      _script += `
      ${item.sql}
      `
    })
    let sql = `/* 系统字段 */
      Declare @UserName nvarchar(50), @FullName nvarchar(50), @RoleID nvarchar(512), @mk_departmentcode nvarchar(512), @mk_organization nvarchar(512), @mk_user_type nvarchar(20), @mk_nation nvarchar(50), @mk_province nvarchar(50), @mk_city nvarchar(50), @mk_district nvarchar(50), @mk_address nvarchar(100), @ErrorCode nvarchar(50), @retmsg nvarchar(4000), @account_id nvarchar(50), @account_year_id nvarchar(50), @account_code nvarchar(50), @account_year_code nvarchar(50), @bid nvarchar(50), @tbid nvarchar(50)
      Select @UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @mk_departmentcode='${departmentcode}', @mk_organization='${organization}', @mk_user_type='${mk_user_type}', @mk_nation='${nation}', @mk_province='${province}', @mk_city='${city}', @mk_district='${district}', @mk_address='${address}', @ErrorCode='', @retmsg='', @account_id='${book.account_id || ''}', @account_year_id='${book.account_year_id || ''}', @account_code='${book.account_code || ''}', @account_year_code='${book.account_year_code || ''}', @bid=''
      /* 发票主表字段 */
      Declare @invoice_type Nvarchar(50), @from_to_name Nvarchar(50), @from_to_tax_no Nvarchar(50), @from_to_addr Nvarchar(100), @from_to_tel Nvarchar(50), @from_to_bank_name Nvarchar(50), @from_to_account_no Nvarchar(50), @from_to_mob Nvarchar(50), @from_to_email Nvarchar(50), @from_to_code Nvarchar(50), @orgname Nvarchar(50), @tax_no Nvarchar(50), @addr Nvarchar(100), @tel Nvarchar(50), @bank_name Nvarchar(50), @account_no Nvarchar(50), @remark Nvarchar(512), @payee Nvarchar(50), @reviewer Nvarchar(50), @drawer Nvarchar(50), @io Nvarchar(50), @orgcode Nvarchar(50), @total_net_amount Decimal(18,2), @total_tax Decimal(18,2), @total_amount Decimal(18,2)
      Select @invoice_type='${invoice_type}', @from_to_name='${from_to_name}', @from_to_tax_no='${from_to_tax_no}', @from_to_addr='${from_to_addr}', @from_to_tel='${from_to_tel}', @from_to_bank_name='${from_to_bank_name}', @from_to_account_no='${from_to_account_no}', @from_to_mob='${from_to_mob}', @from_to_email='${from_to_email}', @from_to_code='${from_to_code}', @orgname='${orgname}', @tax_no='${tax_no}', @addr='${addr}', @tel='${tel}', @bank_name='${bank_name}', @account_no='${account_no}', @remark='${remark}', @payee='${payee}', @reviewer='${reviewer}', @drawer='${drawer}', @io='${io}', @orgcode='${book.orgcode || ''}', @total_net_amount=${_total}, @total_tax=${tax}, @total_amount=${price}
      /* 发票明细临时表 */
      Declare @details_list table (productcode Nvarchar(50), productname Nvarchar(50), spec Nvarchar(50), unit Nvarchar(50), bill_count Decimal(18,10), unitprice Decimal(18,10), amount_line Decimal(18,2), tax_classify_code Nvarchar(50), tax_classify_name Nvarchar(50), tax_rate Decimal(18,2), tax_amount Decimal(18,2), free_tax_mark Nvarchar(50), vat_special_management Nvarchar(50), invoice_lp Nvarchar(50), jskey Nvarchar(50), data_type Nvarchar(50))
      Insert into @details_list (productcode, productname, spec, unit, bill_count, unitprice, amount_line, tax_classify_code, tax_classify_name, tax_rate, tax_amount, free_tax_mark, vat_special_management, invoice_lp, jskey, data_type)
      ${lines}
      /* 自定义脚本 */
      ${_script}
      aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg`
    sql = sql.replace(/@ID@/ig, `'${ID}'`)
    sql = sql.replace(/@BID@/ig, `'${BID}'`)
    sql = sql.replace(/@LoginUID@/ig, `'${sessionStorage.getItem('LoginUID') || ''}'`)
    sql = sql.replace(/@SessionUid@/ig, `'${localStorage.getItem('SessionUid') || ''}'`)
    sql = sql.replace(/@UserID@/ig, `'${sessionStorage.getItem('UserID') || ''}'`)
    sql = sql.replace(/@Appkey@/ig, `'${window.GLOB.appkey || ''}'`)
    sql = sql.replace(/@typename@/ig, `'admin'`)
    if (window.GLOB.externalDatabase !== null) {
      sql = sql.replace(/@db@/ig, window.GLOB.externalDatabase)
    }
    if (sessionStorage.getItem('dataM') === 'true') { // 数据权限
      sql = sql.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, `'Y'`)
    } else {
      sql = sql.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, `''`)
    }
    if (window.GLOB.debugger === true) {
      console.info(sql.replace(/\n\s{6}/ig, '\n'))
    }
    return sql
  }
  getBillMsg = () => {
    const { requireds, invoice_type, details, remark, from_to_addr, addr } = this.state
    return new Promise((resolve, reject) => {
      let error = ''
      if (!invoice_type) {
        error = '请选择发票类型!'
      }
      if (!error) {
        requireds.forEach(item => {
          if (!this.state[item.value] && !error) {
            error = '请输入' + item.label + '!'
          }
        })
      }
      if (!error && remark.length > 512) {
        error = '备注不可超过512个字符!'
      }
      if (!error && from_to_addr.length > 100) {
        error = '购买方地址不可超过100个字符!'
      }
      if (!error && addr.length > 100) {
        error = '销售方地址不可超过100个字符!'
      }
      if (!error) {
        if (details.length === 0) {
          error = '请添加明细!'
        } else {
          details.forEach((line, index) => {
            if (error) return
            if (line.productcode) {
              if (!line.bill_count) {
                error = '明细第' + (index + 1) + '行,请输入数量!'
              } else if (!line.unitprice) {
                error = '明细第' + (index + 1) + '行,请输入单价!'
              }
            } else {
              error = '明细第' + (index + 1) + '行,请选择货物或应税劳务、服务名称!'
            }
          })
        }
      }
      if (error) {
        reject(error)
      } else {
        resolve()
      }
    })
  }
  changeBuyer = (item) => {
    this.setState({
      visible: false,
      from_to_name: item.from_to_name || '',
      from_to_tax_no: item.from_to_tax_no || '',
      from_to_addr: item.from_to_addr || '',
      from_to_tel: item.from_to_tel || '',
      from_to_bank_name: item.from_to_bank_name || '',
      from_to_account_no: item.from_to_account_no || '',
      from_to_mob: item.from_to_mob || '',
      from_to_email: item.from_to_email || '',
      from_to_code: item.from_to_code || '',
    })
  }
  render() {
    const { config, book, loading, invTypes, reqfields, saveType, date, invoice_type, from_to_name, from_to_tax_no, from_to_addr, from_to_tel, from_to_bank_name, from_to_account_no, from_to_mob, from_to_email, orgname, tax_no, addr, tel, bank_name, account_no, remark, reviewer, drawer, payee, details, visible, tax_type } = this.state
    if (!book || (config.wrap.datatype === 'dynamic' && !tax_no)) {
      return <div className="menu-invoice-wrap" style={config.style}>
        <div className="loading-mask">
          <div className="ant-spin-blur"></div>
          <Spin />
        </div>
      </div>
    }
    return (
      <div className="menu-invoice-wrap" style={config.style}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
            <Spin />
          </div> : null
        }
        <div className="inv-action">
          <Button className="mk-bill" loading={saveType === 'bill'} onClick={this.saveBill}>保存单据</Button>
          <Button className="mk-submit" loading={saveType === 'out'} onClick={this.outBill}>提交开票</Button>
        </div>
        <div className="inv-header">
          {invoice_type ? <Select defaultValue={invoice_type} onChange={this.changeType} dropdownClassName="inv-type-select">
            {invTypes.map(item => (
              <Select.Option key={item.value} value={item.value}>{item.label}</Select.Option>
            ))}
          </Select> : <Select placeholder="请选择发票种类" onChange={this.changeType} dropdownClassName="inv-type-select">
            {invTypes.map(item => (
              <Select.Option key={item.value} value={item.value}>{item.label}</Select.Option>
            ))}
          </Select>}
          <div className="date">开票日期:{date}</div>
        </div>
        <div className="inv-body">
          <div className="inv-main-content">
            <div className="inv-buyer">
              <div className="inv-label">购买方</div>
              <div className="inv-content">
                <Form.Item required={reqfields.includes('from_to_name')} label={<>名<span></span>称</>} extra={<EllipsisOutlined onClick={() => this.setState({visible: true})}/>}>
                  <Input placeholder="请输入购买方名称" allowClear value={from_to_name} autoComplete="off" onChange={(e) => this.setState({from_to_name: e.target.value})}/>
                </Form.Item>
                <Form.Item required={reqfields.includes('from_to_tax_no')} label="纳税人识别号">
                  <Input placeholder="请输入购买方纳税人识别号" allowClear value={from_to_tax_no} autoComplete="off" onChange={(e) => this.setState({from_to_tax_no: e.target.value})}/>
                </Form.Item>
                <Form.Item required={reqfields.includes('from_to_addr')} className="mutil-input" label={<>地<span></span>址<span></span>、<span></span>电<span></span>话</>}>
                  <Input placeholder="请输入购买方地址" allowClear value={from_to_addr} autoComplete="off" onChange={(e) => this.setState({from_to_addr: e.target.value})}/>
                  <Input placeholder="请输入购买方电话" allowClear value={from_to_tel} autoComplete="off" onChange={(e) => this.setState({from_to_tel: e.target.value})}/>
                </Form.Item>
                <Form.Item required={reqfields.includes('from_to_bank_name')} className="mutil-input" label="开户行及账号">
                  <Input placeholder="请输入购买方开户行" allowClear value={from_to_bank_name} autoComplete="off" onChange={(e) => this.setState({from_to_bank_name: e.target.value})}/>
                  <Input placeholder="请输入购买方账号" allowClear value={from_to_account_no} autoComplete="off" onChange={(e) => this.setState({from_to_account_no: e.target.value})}/>
                </Form.Item>
              </div>
            </div>
            <div className="inv-notice">
              <div className="inv-label">通知到</div>
              <div className="inv-content">
                <Form.Item required={reqfields.includes('from_to_mob')} label={<>手<span></span>机<span></span>号</>}>
                  <Input placeholder="请输入购买方手机号" allowClear value={from_to_mob} autoComplete="off" onChange={(e) => this.setState({from_to_mob: e.target.value})}/>
                </Form.Item>
                <Form.Item required={reqfields.includes('from_to_email')} label={<>邮<span></span>箱</>}>
                  <Input placeholder="请输入购买方邮箱" allowClear value={from_to_email} autoComplete="off" onChange={(e) => this.setState({from_to_email: e.target.value})}/>
                </Form.Item>
              </div>
            </div>
          </div>
          <div className="inv-details">
            <InvoiceTable data={details} config={config.detail} tax_type={tax_type} onChange={(details) => this.setState({details})}/>
          </div>
          <div className="inv-main-content">
            <div className="inv-buyer">
              <div className="inv-label">销售方</div>
              <div className="inv-content">
                <Form.Item required={reqfields.includes('orgname')} label={<>名<span></span>称</>}>
                  <Input placeholder="请输入销售方名称" value={orgname} autoComplete="off" onChange={(e) => this.setState({orgname: e.target.value})}/>
                </Form.Item>
                <Form.Item required={reqfields.includes('tax_no')} label="纳税人识别号">
                  <Input placeholder="请输入销售方纳税人识别号" disabled value={tax_no} autoComplete="off"/>
                </Form.Item>
                <Form.Item required={reqfields.includes('addr')} className="mutil-input" label={<>地<span></span>址<span></span>、<span></span>电<span></span>话</>}>
                  <Input placeholder="请输入销售方地址" value={addr} autoComplete="off" onChange={(e) => this.setState({addr: e.target.value})}/>
                  <Input placeholder="请输入销售方电话" value={tel} autoComplete="off" onChange={(e) => this.setState({tel: e.target.value})}/>
                </Form.Item>
                <Form.Item required={reqfields.includes('bank_name')} className="mutil-input" label="开户行及账号">
                  <Input placeholder="请输入销售方开户行" value={bank_name} autoComplete="off" onChange={(e) => this.setState({bank_name: e.target.value})}/>
                  <Input placeholder="请输入销售方账号" value={account_no} autoComplete="off" onChange={(e) => this.setState({account_no: e.target.value})}/>
                </Form.Item>
              </div>
            </div>
            <div className="inv-notice">
              <div className="inv-label">备注</div>
              <div className="inv-content" style={{paddingTop: '30px'}}>
                <Form.Item label="">
                  <Input.TextArea placeholder="请输入备注" autoSize={{ minRows: 4, maxRows: 4 }} value={remark} autoComplete="off" onChange={(e) => this.setState({remark: e.target.value})}/>
                </Form.Item>
              </div>
            </div>
          </div>
        </div>
        <div className="inv-tail">
          <Form.Item label="收款人">
            <Input placeholder="收款人" value={payee} autoComplete="off" onChange={(e) => this.setState({payee: e.target.value})}/>
          </Form.Item>
          <Form.Item label="复核人">
            <Input placeholder="复核人" value={reviewer} autoComplete="off" onChange={(e) => this.setState({reviewer: e.target.value})}/>
          </Form.Item>
          <Form.Item label="开票人">
            <Input placeholder="开票人" value={drawer} autoComplete="off" onChange={(e) => this.setState({drawer: e.target.value})}/>
          </Form.Item>
        </div>
        <Modal
          title="客户信息"
          visible={visible}
          width="70vw"
          maskClosable={false}
          onCancel={() => { this.setState({ visible: false }) }}
          footer={null}
        >
          <SubTable config={config.buyer} onChange={this.changeBuyer}/>
        </Modal>
      </div>
    )
  }
}
export default InvoiceModule
src/tabviews/custom/components/module/invoice/index.scss
New file
@@ -0,0 +1,292 @@
.menu-invoice-wrap {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  color: #000000;
  min-height: 500px;
  --inv-color: #13509c;
  .inv-action {
    text-align: right;
    margin-right: 30px;
    .ant-btn {
      margin-left: 15px;
      margin-bottom: 5px;
      height: 30px;
    }
    .mk-bill:hover, .mk-bill:active, .mk-bill:focus {
      color: var(--mk-sys-color);
      border-color: var(--mk-sys-color);
    }
    .mk-submit, .mk-submit:hover, .mk-submit:active, .mk-submit:focus {
      color: #fff;
      background-color: var(--mk-sys-color);
      border-color: var(--mk-sys-color);
    }
  }
  .inv-header {
    text-align: center;
    position: relative;
    height: 70px;
    margin-right: 30px;
    .ant-select {
      width: 390px;
      border: none;
      .ant-select-selection {
        border: none;
        box-shadow: none;
        font-size: 25px;
        text-align: center;
        background-color: transparent;
        .ant-select-selection-selected-value {
          float: none;
          text-align: center;
          font-family: kaiti;
          color: var(--inv-color, #13509c);
        }
        .ant-select-selection__placeholder {
          text-align: center;
          font-family: kaiti;
          height: 30px;
          line-height: 30px;
          margin-top: -12px;
        }
      }
    }
    .ant-select::before, .ant-select::after {
      content: '';
      display: block;
      width: 100%;
      position: absolute;
      border-top: var(--inv-color, #13509c) 1px solid;
      border-bottom: var(--inv-color, #13509c) 1px solid;
      height: 1px;
    }
    .ant-select::before {
      bottom: -10px;
    }
    .ant-select::after {
      bottom: -15px;
    }
    .date {
      position: absolute;
      right: 100px;
      top: 5px;
      color: var(--inv-color, #13509c);
    }
  }
  .ant-input {
    border-top: none;
    border-left: none;
    border-right: none;
    border-radius: 0;
    box-shadow: none!important;
    height: 28px;
    font-size: 13px;
    background: transparent;
  }
  .ant-input:hover, .ant-input:active, .ant-input:focus {
    border-color: var(--inv-color, #13509c)!important;
  }
  .ant-input.ant-input-disabled {
    cursor: text;
    color: rgba(0, 0, 0, 0.85);
    background: transparent!important;
  }
  .ant-input-number {
    border-top: none;
    border-left: none;
    border-right: none;
    border-radius: 0;
    box-shadow: none!important;
    height: 28px;
    font-size: 13px;
    .ant-input-number-handler-wrap {
      display: none;
    }
  }
  .ant-input-number:hover, .ant-input-number:active, .ant-input-number:focus {
    border-color: var(--inv-color, #13509c)!important;
  }
  .inv-body {
    border: var(--inv-color, #13509c) 1px solid;
    font-size: 13px;
    margin-right: 30px;
    .inv-main-content {
      display: flex;
      .inv-buyer, .inv-notice {
        width: 50%;
        display: flex;
        .inv-label {
          color: var(--inv-color, #13509c);
          width: 6.25%;
          display: flex;
          flex-direction: column;
          writing-mode: vertical-rl;
          justify-content: center;
          align-items: center;
          letter-spacing: 5px;
          border-right: var(--inv-color, #13509c) 1px solid;
        }
        .inv-content {
          flex: 1;
          padding: 6px 0;
          .ant-form-item {
            display: flex;
            padding: 0 5px 0 15px;
            font-size: 13px;
            margin-bottom: 5px;
            .ant-form-item-label {
              line-height: 30px;
              text-align: justify;
              label {
                display: flex;
                color: var(--inv-color, #13509c);
                span {
                  display: inline-block;
                  flex: 1;
                }
              }
            }
            .ant-form-item-control-wrapper {
              width: 100%;
              .ant-form-item-control {
                line-height: 30px;
                .ant-form-extra {
                  position: absolute;
                  top: 1px;
                  right: 10px;
                  font-size: 16px;
                  .anticon {
                    cursor: pointer;
                  }
                }
                .ant-input-affix-wrapper:not(:hover) {
                  .ant-input-suffix {
                    opacity: 0;
                  }
                }
              }
            }
          }
          .ant-form-item.mutil-input {
            .ant-form-item-children {
              display: flex;
            }
          }
        }
      }
      .inv-buyer {
        border-right: var(--inv-color, #13509c) 1px solid;
        .ant-form-item-label {
          width: 103px;
          min-width: 103px;
          label:not(.ant-form-item-required) {
            padding-left: 11px;
          }
          .ant-form-item-required::before {
            line-height: 30px;
          }
        }
      }
      .inv-notice {
        .inv-content {
          padding-top: 45px;
        }
        .ant-form-item-label {
          width: 75px;
          min-width: 75px;
        }
      }
    }
    .inv-details {
      border-top: var(--inv-color, #13509c) 1px solid;
      border-bottom: var(--inv-color, #13509c) 1px solid;
    }
  }
  .inv-tail {
    display: flex;
    padding-top: 10px;
    margin-right: 30px;
    .ant-form-item {
      flex: 1;
      display: flex;
      font-size: 13px;
      .ant-form-item-label, .ant-form-item-control-wrapper {
        width: 40%;
      }
    }
  }
  .loading-mask {
    position: absolute;
    left: 0px;
    top: 0;
    right: 0px;
    bottom: 0px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: justify;
    z-index: 1;
    .ant-spin-blur {
      position: absolute;
      width: 100%;
      height: 100%;
      opacity: 0.5;
      background: #ffffff;
    }
  }
}
.inv-table {
  .ant-table .ant-table-tbody tr:hover td {
    background-color: var(--mk-sys-color1);
  }
}
.tb-search-wrap {
  .search-item {
    display: flex;
    float: left;
    width: 350px;
    align-items: center;
    justify-content: flex-end;
    .ant-input {
      width: 200px;
    }
  }
  .ant-btn {
    margin-bottom: 20px;
    margin-left: 50px;
    background-color: var(--mk-sys-color);
    border-color: var(--mk-sys-color);
    color: #ffffff;
  }
  .ant-btn:hover, .ant-btn:active {
    background-color: var(--mk-sys-color);
    border-color: var(--mk-sys-color);
    color: #ffffff;
    opacity: 0.9;
  }
}
.inv-type-select {
  .ant-select-dropdown-menu-item {
    text-align: center;
  }
}
src/tabviews/custom/components/module/invoice/invoiceTable/index.jsx
New file
@@ -0,0 +1,420 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Input, InputNumber, notification, Modal } from 'antd'
import { EllipsisOutlined } from '@ant-design/icons'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import SubTable from '../subTable'
import './index.scss'
class DetailLine extends React.Component {
  state = {
    bill_count: 0,
    unitprice: 0,
    amount_line: 0,
    visible: false
  }
  UNSAFE_componentWillMount() {
    let { line } = this.props
    this.setState({
      bill_count: line.bill_count || '',
      unitprice: line.unitprice || '',
      amount_line: line.amount_line || ''
    })
  }
  onChange = (value, key) => {
    let line = {...this.props.line}
    if (['bill_count', 'unitprice', 'amount_line'].includes(key)) {
      line[key] = value || 0
      if (isNaN(line[key])) {
        line[key] = 0
      }
      if (line[key]) {
        if (key === 'bill_count') {
          line[key] = Math.round(line[key] * 10000000000) / 10000000000
          if (line.unitprice) {
            line.amount_line = Math.round(line.unitprice * line.bill_count * 100) / 100
          }
        } else if (key === 'unitprice') {
          line[key] = Math.round(line[key] * 10000000000) / 10000000000
          if (line.bill_count) {
            line.amount_line = Math.round(line.unitprice * line.bill_count * 100) / 100
          }
        } else if (key === 'amount_line') {
          line[key] = Math.round(line[key] * 100) / 100
          if (line.bill_count) {
            line.unitprice = Math.round(line.amount_line / line.bill_count * 10000000000) / 10000000000
          } else if (line.unitprice) {
            line.bill_count = Math.round(line.amount_line / line.unitprice * 10000000000) / 10000000000
          }
        }
      } else if (key === 'amount_line') {
        line.bill_count = 0
      }
      if (line.amount_line) {
        line.tax_amount = line.amount_line - line.amount_line / (line.tax_rate + 1)
        line.tax_amount = Math.round(line.tax_amount * 100) / 100
      } else {
        line.tax_amount = 0
      }
    } else {
      line[key] = value
    }
    this.setState({
      bill_count: line.bill_count || '',
      unitprice: line.unitprice || '',
      amount_line: line.amount_line || ''
    })
    this.props.changeLine(line, key)
  }
  render() {
    const { line, delLine, trigger } = this.props
    const { bill_count, unitprice, amount_line } = this.state
    return <div className="mk-tr active">
      <div className="mk-td">
        <div className="mk-input">{line.productname || ''}<EllipsisOutlined onClick={trigger}/></div>
      </div>
      <div className="mk-td">
        <Input defaultValue={line.spec || ''} onChange={(e) => this.onChange(e.target.value, 'spec')}/>
      </div>
      <div className="mk-td">
        <Input defaultValue={line.unit || ''} onChange={(e) => this.onChange(e.target.value, 'unit')}/>
      </div>
      <div className="mk-td">
        <InputNumber value={bill_count} formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')} parser={value => value.replace(/,*/g, '')} onChange={(val) => this.setState({bill_count: val})} onBlur={() => this.onChange(bill_count, 'bill_count')}/>
      </div>
      <div className="mk-td">
        <InputNumber value={unitprice} formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')} parser={value => value.replace(/,*/g, '')} onChange={(val) => this.setState({unitprice: val})} onBlur={() => this.onChange(unitprice, 'unitprice')}/>
      </div>
      <div className="mk-td">
        <InputNumber value={amount_line} formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')} parser={value => value.replace(/,*/g, '')} onChange={(val) => this.setState({amount_line: val})} onBlur={() => this.onChange(amount_line, 'amount_line')}/>
      </div>
      <div className="mk-td mk-right">{line.tax_name}</div>
      <div className="mk-td mk-right">{line.tax_amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')} <span className="del-line" onClick={() => delLine(line.uuid)}></span> </div>
    </div>
  }
}
class InvoiceTable extends Component {
  static propTpyes = {
    config: PropTypes.object,
    data: PropTypes.any,
    onChange: PropTypes.func
  }
  state = {
    data: [],
    editKey: '',
    total: {}
  }
  UNSAFE_componentWillMount () {
    const { data } = this.props
    let _data = fromJS(data).toJS()
    if (!_data.length) {
      _data = [{uuid: Utils.getguid(), productname: '', spec: '', unit: '', bill_count: '', unitprice: 0, amount_line: 0, tax_rate: '', tax_name: '', tax_amount: 0}]
    }
    this.setState({
      data: _data
    }, () => {
      this.getTotal(_data)
    })
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  componentDidMount () {
    MKEmitter.addListener('resetDetails', this.resetDetails)
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('resetDetails', this.resetDetails)
  }
  changeMoneyToChinese = (money) => {
    let cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
    let cnIntRadice = ['', '拾', '佰', '仟']
    let cnIntUnits = ['', '万', '亿', '兆']
    let cnDecUnits = ['角', '分', '毫', '厘']
    let cnInteger = '整'
    let cnIntLast = '元'
    let maxNum = 999999999999999.9999 // 最大处理的数字
    let IntegerNum = null
    let DecimalNum = null
    let ChineseStr = ''
    let parts = null // 分离金额后用的数组,预定义
    let Symbol = ''  // 正负值标记
    if (money === 0) return ''
    if (money >= maxNum) return '超出最大处理数字'
    if(money < 0) {
      money = -money
      Symbol = '负'
    }
    money = money.toString() // 转换为字符串
    if (money.indexOf('.') === -1) {
      IntegerNum = money
      DecimalNum = ''
    } else {
      parts = money.split('.')
      IntegerNum = parts[0]
      DecimalNum = parts[1].substr(0, 4)
    }
    if (parseInt(IntegerNum, 10) > 0) { // 获取整型部分转换
      let zeroCount = 0
      let IntLen = IntegerNum.length
      for (let i = 0; i < IntLen; i++) {
        let n = IntegerNum.substr(i, 1)
        let p = IntLen - i - 1
        let q = p / 4
        let m = p % 4
        if (n === '0') {
          zeroCount++
        } else {
          if (zeroCount > 0) {
            ChineseStr += cnNums[0]
          }
          zeroCount = 0 // 归零
          ChineseStr += cnNums[parseInt(n)] + cnIntRadice[m]
        }
        if (m === 0 && zeroCount < 4) {
          ChineseStr += cnIntUnits[q]
        }
      }
      ChineseStr += cnIntLast
    }
    if (DecimalNum !== '') { // 小数部分
      let decLen = DecimalNum.length
      for (let i = 0; i < decLen; i++) {
        let n = DecimalNum.substr(i, 1)
        if (n !== '0') {
          ChineseStr += cnNums[Number(n)] + cnDecUnits[i]
        }
      }
    }
    if (ChineseStr === '') {
      ChineseStr += cnNums[0] + cnIntLast + cnInteger
    } else if (DecimalNum === '') {
      ChineseStr += cnInteger
    }
    ChineseStr = Symbol + ChineseStr
    return ChineseStr
  }
  getTotal = (data) => {
    let price = 0
    let tax = 0
    data.forEach(item => {
      if (!item.productcode) return
      price += item.amount_line * 100
      tax += item.tax_amount * 100
    })
    price = price / 100
    tax = tax / 100
    this.setState({total: {price, tax, sum: price, sumName: this.changeMoneyToChinese(price)}})
  }
  resetDetails = (data) => {
    let _data = fromJS(data).toJS()
    if (!_data.length) {
      _data = [{uuid: Utils.getguid(), productname: '', spec: '', unit: '', bill_count: '', unitprice: 0, amount_line: 0, tax_rate: '', tax_name: '', tax_amount: 0}]
    }
    this.setState({data: _data}, () => {
      this.getTotal(_data)
    })
  }
  addLine = () => {
    const { data } = this.state
    let line = {uuid: Utils.getguid(), productname: '', spec: '', unit: '', bill_count: '', unitprice: 0, amount_line: 0, tax_rate: '', tax_name: '', tax_amount: 0}
    this.setState({data: [...data, line]})
  }
  delLine = () => {
    const { editKey, data } = this.state
    if (data.length === 1) {
      notification.warning({
        top: 92,
        message: '至少保留一行明细!',
        duration: 3
      })
      return
    }
    let _data = data.filter(item => item.uuid !== editKey)
    this.setState({data: _data}, () => {
      this.getTotal(_data)
    })
    this.props.onChange(_data)
  }
  changeLine = (record) => {
    const { editKey, data } = this.state
    let _data = data.map(item => {
      if (item.uuid === editKey) {
        return record
      } else {
        return item
      }
    })
    this.setState({data: _data}, () => {
      this.getTotal(_data)
    })
    this.props.onChange(_data)
  }
  checkLine = (uuid) => {
    this.setState({editKey: uuid})
  }
  changeDetail = (prod) => {
    const { editKey, data } = this.state
    let _data = data.map(item => {
      if (item.uuid === editKey) {
        item.productname = prod.productname
        item.spec = prod.spec
        item.unit = prod.unit
        item.unitprice = prod.unitprice
        item.tax_rate = prod.tax_rate || 0
        item.tax_name = prod.tax_rate * 100 + '%'
        item.free_tax_mark = prod.free_tax_mark || ''
        item.vat_special_management = prod.vat_special_management || ''
        if (prod.vat_special_management && prod.free_tax_mark === 'true') {
          item.tax_name = prod.vat_special_management
        }
        item.productcode = prod.productcode
        item.Description = prod.Description
        item.tax_classify_code = prod.tax_classify_code
        item.tax_classify_name = prod.tax_classify_name
        if (item.bill_count && item.unitprice) {
          item.amount_line = Math.round(item.unitprice * item.bill_count * 100) / 100
        }
        if (item.amount_line) {
          item.tax_amount = item.amount_line - item.amount_line / (item.tax_rate + 1)
          item.tax_amount = Math.round(item.tax_amount * 100) / 100
        } else {
          item.tax_amount = 0
        }
      }
      return item
    })
    this.setState({data: _data, editKey: '', visible: false}, () => {
      this.setState({editKey: editKey})
      this.getTotal(_data)
    })
    this.props.onChange(_data)
  }
  render() {
    const { config, tax_type } = this.props
    const { editKey, data, total, visible } = this.state
    return (
      <div className="detail-wrap">
        <span className="plus-line" onClick={this.addLine}></span>
        <div className="mk-th">
          <div className="mk-td">货物或应税劳务、服务名称</div>
          <div className="mk-td">规格型号</div>
          <div className="mk-td">单位</div>
          <div className="mk-td">数量</div>
          <div className="mk-td">单价(含税)</div>
          <div className="mk-td">金额(含税)</div>
          <div className="mk-td">税率</div>
          <div className="mk-td">税额</div>
        </div>
        {data.map(item => {
          if (editKey === item.uuid) {
            return <DetailLine key={item.uuid} line={item} changeLine={this.changeLine} delLine={this.delLine} trigger={() => this.setState({visible: true})}/>
          }
          return <div className="mk-tr" key={item.uuid} onClick={() => this.checkLine(item.uuid)}>
            <div className="mk-td mk-left">{item.productname || ''}</div>
            <div className="mk-td mk-left">{item.spec || ''}</div>
            <div className="mk-td mk-left">{item.unit || ''}</div>
            <div className="mk-td mk-right">{`${item.bill_count || ''}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}</div>
            <div className="mk-td mk-right">{`${item.unitprice || ''}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}</div>
            <div className="mk-td mk-right">{`${item.amount_line || ''}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}</div>
            <div className="mk-td mk-right">{item.tax_name}</div>
            <div className="mk-td mk-right">{item.tax_amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')}<span className="del-line" onClick={() => this.delLine(item.uuid)}></span></div>
          </div>
        })}
        <div className="mk-total">
          <div className="mk-td">合计</div>
          <div className="mk-td"></div>
          <div className="mk-td"></div>
          <div className="mk-td"></div>
          <div className="mk-td"></div>
          <div className="mk-td">¥{`${total.price}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}</div>
          <div className="mk-td"></div>
          <div className="mk-td">¥{`${total.tax}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}</div>
        </div>
        <div className="mk-upcase">
          <div className="mk-td">价税合计(大写)</div>
          <div className="mk-td">{total.sumName}</div>
          <div className="mk-td">(小写)¥{`${total.sum}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}</div>
        </div>
        <Modal
          title="商品信息"
          visible={visible}
          width="75vw"
          maskClosable={false}
          onCancel={() => { this.setState({ visible: false }) }}
          footer={null}
        >
          <SubTable config={config} tax_type={tax_type} onChange={this.changeDetail}/>
        </Modal>
      </div>
    )
  }
}
export default InvoiceTable
src/tabviews/custom/components/module/invoice/invoiceTable/index.scss
New file
@@ -0,0 +1,131 @@
.detail-wrap {
  position: relative;
  .plus-line, .del-line {
    position: absolute;
    right: -40px;
    top: 5px;
    font-size: 26px;
    border: 1px solid var(--inv-color, #13509c);
    width: 30px;
    height: 30px;
    transition: all 0.3s;
    cursor: pointer;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .plus-line::before, .del-line::before {
    content: ' ';
    position: absolute;
    top: 13px;
    width: 12px;
    height: 1px;
    background: var(--inv-color, #13509c);
    transition: all 0.3s;
  }
  .plus-line::after {
    content: ' ';
    position: absolute;
    top: 13px;
    width: 12px;
    height: 1px;
    background: var(--inv-color, #13509c);
    transform: rotate(90deg);
    transition: all 0.3s;
  }
  .del-line {
    display: none;
  }
  .mk-tr:hover {
    .del-line {
      display: flex;
    }
    .del-line::after {
      content: ' ';
      position: absolute;
      top: -6px;
      width: 12px;
      height: 40px;
      left: -12px;
    }
  }
  .mk-th, .mk-tr, .mk-total {
    position: relative;
    display: flex;
    border-bottom: var(--inv-color, #13509c) 1px solid;
    .mk-td {
      width: 10%;
      height: 40px;
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 0 5px;
    }
    .mk-td:not(:last-child) {
      border-right: var(--inv-color, #13509c) 1px solid;
    }
    .mk-td:first-child {
      width: 20%;
    }
    .mk-td:nth-child(2), .mk-td:nth-child(6) {
      width: 15%;
    }
    .mk-left {
      justify-content: start;
    }
    .mk-right {
      justify-content: end;
    }
    .mk-input {
      position: relative;
      height: 28px;
      width: 100%;
      background: #ffffff;
      border-bottom: 1px solid #d9d9d9;
      transition: all 0.3s;
      .anticon-ellipsis {
        position: absolute;
        right: 0px;
        padding: 5px 10px;
        cursor: pointer;
      }
    }
    .mk-input:hover {
      border-color: var(--inv-color, #13509c);
    }
    .ant-input {
      background: #ffffff;
    }
  }
  .mk-tr.active, .mk-tr:hover {
    background: var(--mk-sys-color1);
  }
  .mk-upcase {
    display: flex;
    .mk-td {
      width: 40%;
      height: 40px;
      display: flex;
      align-items: center;
      padding: 0 20px;
    }
    .mk-td:first-child {
      width: 20%;
      padding: 0;
      border-right: var(--inv-color, #13509c) 1px solid;
      justify-content: center;
    }
    .mk-td:last-child {
      justify-content: end;
    }
  }
}
src/tabviews/custom/components/module/invoice/subTable/index.jsx
New file
@@ -0,0 +1,250 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Table, Input, Button } from 'antd'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
// import './index.scss'
class SearchWrap extends Component {
  static propTpyes = {
    search: PropTypes.array,
    onChange: PropTypes.func
  }
  state = {
    search: []
  }
  UNSAFE_componentWillMount () {
    const { search } = this.props
    this.setState({
      search: fromJS(search).toJS()
    })
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  searchChange = (val, key) => {
    const { search } = this.state
    this.setState({search: search.map(item => {
      if (item.field === key) {
        item.value = val
      }
      return item
    })})
  }
  trigger = () => {
    const { search } = this.state
    this.props.onChange(search)
  }
  render() {
    const { search } = this.state
    return (
      <div className="tb-search-wrap">
        {search.map(item => (
          <div className="search-item" key={item.field}>
            {item.label}:
            <Input autoComplete="off" onChange={(e) => this.searchChange(e.target.value, item.field)}/>
          </div>
        ))}
        <Button onClick={this.trigger}>搜索</Button>
      </div>
    )
  }
}
class SubTable extends Component {
  static propTpyes = {
    config: PropTypes.object
  }
  state = {
    pageIndex: 1,         // 初始页面索引
    pageSize: 10,         // 每页数据条数
    columns: null,        // 显示列
    pageOptions: [],
    search: []
  }
  UNSAFE_componentWillMount () {
    const { config } = this.props
    let _columns = []
    config.columns.forEach(item => {
      if (item.Hide === 'true') return
      _columns.push({
        align: 'center',
        dataIndex: item.field,
        title: item.label,
        sorter: false,
        width: item.Width || 120
      })
    })
    let size = (config.setting.pageSize || 10) + ''
    let pageOptions = ['10', '25', '50', '100', '500', '1000']
    if (!pageOptions.includes(size)) {
      pageOptions.push(size)
      pageOptions = pageOptions.sort((a, b) => a - b)
    }
    this.setState({
      pageSize: config.pageSize || 10,
      pageOptions,
      search: fromJS(config.search).toJS(),
      columns: _columns
    })
  }
  componentDidMount() {
    const { config } = this.props
    if (config.setting.onload === 'true') {
      this.loadData()
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  /**
   * @description 数据加载
   */
  async loadData () {
    const { config, tax_type } = this.props
    const { search, pageIndex, pageSize } = this.state
    this.setState({
      loading: true
    })
    let searches = search.map(item => ({
      key: item.field,
      match: item.match,
      type: item.type,
      value: item.value || ''
    }))
    let param = UtilsDM.getQueryDataParams(config.setting, searches, config.setting.order, pageIndex, pageSize, '')
    this.requestId = config.uuid + new Date().getTime()
    let result = await Api.genericInterface(param, config.setting.js_script, '', this.requestId)
    if (result.status) {
      if (result.$requestId && this.requestId !== result.$requestId) return
      let start = pageSize * (pageIndex - 1) + 1
      let data = result.data.map((item, index) => {
        item.key = index
        item.$Index = start + index + ''
        if (tax_type) {
          item.tax_rate = tax_type === 'special_invoice' ? item.general_tax_rate : item.small_tax_rate
          item.tax_rate = isNaN(item.tax_rate) ? 0 : +item.tax_rate
        }
        return item
      })
      let total = result.total || 0
      if (config.setting.custompage && data.length) {
        total = data[data.length - 1].mk_total || 0
      }
      this.setState({
        data: data,
        total: total,
        loading: false
      })
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      UtilsDM.queryFail(result)
    }
  }
  changeTable = (pagination) => {
    this.setState({
      pageIndex: pagination.current,
      pageSize: pagination.pageSize,
    }, () => {
      this.loadData()
    })
  }
  doubleClickLine = (record) => {
    this.props.onChange(record)
  }
  searchChange = (search) => {
    this.setState({search: search}, () => {
      this.loadData()
    })
  }
  render() {
    const { pageIndex, pageSize, pageOptions, columns, total, loading, data, search } = this.state
    return (
      <>
        <SearchWrap search={search} onChange={this.searchChange}/>
        <Table
          className="inv-table"
          columns={columns}
          dataSource={data}
          bordered={true}
          loading={loading}
          onRow={(record) => {
            return {
              onDoubleClick: () => {this.doubleClickLine(record)}
            }
          }}
          onChange={this.changeTable}
          pagination={{
            current: pageIndex,
            pageSize: pageSize,
            pageSizeOptions: pageOptions,
            showSizeChanger: true,
            total: total || 0,
            showTotal: (total, range) => `${range[0]}-${range[1]} 共 ${total} 条`
          }}
        />
      </>
    )
  }
}
export default SubTable
src/tabviews/custom/components/module/invoice/subTable/index.scss
src/tabviews/custom/components/module/voucher/index.jsx
@@ -12,10 +12,8 @@
import ResetRemark from './resetRemark'
import ResetAttach from './resetAttach'
import LoadFromTemp from './loadFromTemp'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const PrintVoucher = asyncComponent(() => import('./printVoucher'))
const { confirm } = Modal
class VoucherModule extends Component {
@@ -39,6 +37,8 @@
    username: sessionStorage.getItem('User_Name'),
    remark: '',
    attachments: 0,
    vouAduited: false,
    vouReadOnly: false,
    attachlist: [],
    oriAttachs: [],
    tempTypes: [],
@@ -304,6 +304,7 @@
      let data = []
      if (res.voucher) {
        let disabled = res.voucher_status === 'true'
        data = res.voucher.map(line => {
          line.uuid = line.subject_id || ''
@@ -320,6 +321,7 @@
          line.exratename = line.foreign_exratename || ''
          line.local_currency = line.local_exratecode || ''
          line.foreign_currency_type = line.foreign_type || ''
          line.$disabled = disabled
          if (line.sup) {
            line.supAccounts = line.sup.map(cell => {
@@ -351,7 +353,9 @@
          data: data,
          attachlist: files,
          vouDate: res.fibvoucherdate ? moment(res.fibvoucherdate, 'YYYY-MM-DD') : null,
          charType: res.voucher_class,
          charType: res.voucher_class || 'keeping',
          vouAduited: res.voucher_status === 'true' || res.read_only === 'true',
          vouReadOnly: res.read_only === 'true',
          charName: res.voucher_char,
          charInt: res.voucher_char_int,
          // orgcode: res.orgcode,
@@ -1059,10 +1063,6 @@
    this.setState({status: 'change', attachlist: vals, attachments: num})
  }
  triggermore = () => {
  }
  triggerclose = () => {
    const { config, status } = this.state
@@ -1139,72 +1139,233 @@
    })
  }
  render() {
    const { type, status, loading, config, orgcode, orgname, typeOptions, tempTypes, charType, charName, charInt, data, vouDate, username, remark, attachments, title, attachlist, tempTypeClass } = this.state
  triggerPrint = () => {
    const { config, BID } = this.state
    return (
      <div className="menu-voucher-wrap" style={config.style}>
        {type === 'createVoucher' ? <div className="voucher-header">
          <Button className="add-background header-btn" disabled={status === 'empty'} onClick={() => this.triggersave('add')}>保存并新增</Button>
          <Button className="add-background header-btn" disabled={status === 'empty' || status === 'saved'} onClick={() => this.triggersave()}>保存</Button>
          <PrintVoucher ID={config.uuid + 'print'} data={data} orgname={orgname} vouDate={vouDate} charName={charName} charInt={charInt} attachments={attachments} disabled={status !== 'saved'}/>
          <Dropdown overlay={<div className="mk-voucher-dropdown-wrap">
            <SaveAsTemp tempTypes={tempTypes} onChange={this.triggerTempsave}/>
            <div className="split"></div>
            <LoadFromTemp tempTypes={tempTypes} onChange={this.triggerTempLoad}/>
          </div>} trigger={['click']}>
            <Button className="out-background header-btn" onClick={this.triggermore}>更多</Button>
          </Dropdown>
        </div> : null}
        {type === 'checkVoucher' ? <div className="voucher-header">
          <Button className="add-background header-btn" disabled={status === 'empty' || status === 'saved'} onClick={() => this.triggersave()}>保存</Button>
          <PrintVoucher ID={config.uuid + 'print'} data={data} orgname={orgname} vouDate={vouDate} charName={charName} charInt={charInt} attachments={attachments} disabled={status !== 'saved'}/>
          <Button className="out-background header-btn" onClick={this.triggerclose}>关闭</Button>
        </div> : null}
        <div className="voucher-body" style={{padding: `0px ${config.wrap.space || 0}px`}}>
          {type === 'createVoucher' || type === 'checkVoucher' ? <div className="pre-wrap">
            <div className="voucher-code">
              <Select value={charType} dropdownClassName="mk-vcode-dropdown" onChange={(val, option) => this.setState({status: 'change', charType: val, charName: option.props.charName, charInt: option.props.charint})}>
                {typeOptions.map(option =>
                  <Select.Option key={option.id} value={option.voucher_class} charName={option.voucher_char} charint={option.voucher_char_int}>{option.voucher_char}</Select.Option>
                )}
              </Select>
              <InputNumber precision={0} min={1} value={charInt} autoComplete="off" onChange={(val) => this.setState({status: 'change', charInt: val})}/> 号
    if (!config.wrap.printTemp) {
      notification.warning({
        top: 92,
        message: '尚未设置打印模板!',
        duration: 5
      })
      return
    }
    window.open('#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify({ id: BID, tempId: config.wrap.printTemp, pageId: config.$pageId || '', dataM: sessionStorage.getItem('dataM') }))))
  }
  triggerAduit = () => {
    const { book, BID, voucherCode, username } = this.state
    if (!book) {
      notification.warning({
        top: 92,
        message: '请选择账套!',
        duration: 5
      })
      return
    }
    let param = {
      func: 's_fcc_voucher_sub',
      ID: BID,
      status: 10,
      statusname: '已审核',
      voucher_code: voucherCode || '',
      username: username,
      fullname: sessionStorage.getItem('Full_Name') || ''
    }
    const that = this
    confirm({
      content: '确定要审核吗?',
      onOk() {
        Api.genericInterface(param).then(res => {
          if (!res.status) {
            notification.warning({
              top: 92,
              message: res.message,
              duration: 5
            })
            return
          }
          notification.success({
            top: 92,
            message: '已经通过审核',
            duration: 2
          })
          that.getVoucher()
        })
      },
      onCancel() {}
    })
  }
  triggerUnAduit = () => {
    const { book, BID, voucherCode, username } = this.state
    if (!book) {
      notification.warning({
        top: 92,
        message: '请选择账套!',
        duration: 5
      })
      return
    }
    let param = {
      func: 's_fcc_voucher_sub',
      ID: BID,
      status: 0,
      statusname: '取消审核',
      voucher_code: voucherCode || '',
      username: username,
      fullname: sessionStorage.getItem('Full_Name') || ''
    }
    const that = this
    confirm({
      content: '确认要取消审核吗?',
      onOk() {
        Api.genericInterface(param).then(res => {
          if (!res.status) {
            notification.warning({
              top: 92,
              message: res.message,
              duration: 5
            })
            return
          }
          notification.success({
            top: 92,
            message: '审核已取消',
            duration: 2
          })
          that.getVoucher()
        })
      },
      onCancel() {}
    })
  }
  render() {
    const { type, status, loading, config, orgcode, typeOptions, tempTypes, charType, charInt, data, vouDate, username, remark, attachments, title, attachlist, tempTypeClass, vouAduited, vouReadOnly, voucherCode } = this.state
    if (type === 'checkVoucher') {
      return (
        <div className="menu-voucher-wrap" style={config.style}>
          <div className="voucher-header" style={{padding: `10px ${config.wrap.space || 0}px`}}>
            {!vouAduited ? <Button disabled={status === 'empty' || status === 'saved'} onClick={() => this.triggersave()}>保存</Button> : null}
            {/* <PrintVoucher ID={config.uuid + 'print'} data={data} orgname={orgname} vouDate={vouDate} charName={charName} charInt={charInt} attachments={attachments} disabled={status !== 'saved'}/> */}
            <Button disabled={status !== 'saved'} onClick={this.triggerPrint}>打印</Button>
            {vouAduited && !vouReadOnly ? <Button onClick={this.triggerUnAduit}>取消审核</Button> : null}
            {!vouAduited && !vouReadOnly ? <Button onClick={this.triggerAduit}>审核</Button> : null}
            <Button onClick={this.triggerclose}>关闭</Button>
          </div>
          <div className="voucher-body" style={{padding: `0px ${config.wrap.space || 0}px`}}>
            <div className="pre-wrap">
              <div className="voucher-number">
                凭证号:{voucherCode}
              </div>
              <div className="voucher-code">
                <Select value={charType} disabled={vouAduited} dropdownClassName="mk-vcode-dropdown" onChange={(val, option) => this.setState({status: 'change', charType: val, charName: option.props.charname, charInt: option.props.charint})}>
                  {typeOptions.map(option =>
                    <Select.Option key={option.id} value={option.voucher_class} charname={option.voucher_char} charint={option.voucher_char_int}>{option.voucher_char}</Select.Option>
                  )}
                </Select>
                <InputNumber precision={0} min={1} disabled={vouAduited} value={charInt} autoComplete="off" onChange={(val) => this.setState({status: 'change', charInt: val})}/> 号
              </div>
              <div className="voucher-date">
                日期:<DatePicker value={vouDate} disabled={vouAduited} onChange={this.changeVouDate}/>
              </div>
              <div className="voucher-text">
                <Input value={title} placeholder={vouAduited ? '' : '凭证文本'} disabled={vouAduited} autoComplete="off" onChange={(e) => this.setState({status: 'change', title: e.target.value})}/>
              </div>
              <div className="voucher-affix">
                附单据 <InputNumber precision={0} disabled={vouAduited} value={attachments || 0} autoComplete="off" onChange={this.changeAttach}/> 张
                <ResetAttach config={config} disabled={vouAduited} orgcode={orgcode} voucherCode={this.state.voucherCode} attachlist={attachlist} onChange={this.resetAttachList}/>
                <ResetRemark remark={remark} disabled={vouAduited} ID={config.uuid + 'remark'} onChange={(val) => this.setState({status: 'change', remark: val})}/>
              </div>
            </div>
            <div className="voucher-date">
              日期:<DatePicker value={vouDate} onChange={this.changeVouDate}/>
            </div>
            <div className="voucher-text">
              <Input value={title} placeholder="凭证文本" autoComplete="off" onChange={(e) => this.setState({status: 'change', title: e.target.value})}/>
            </div>
            <div className="voucher-affix">
              附单据 <InputNumber precision={0} value={attachments || 0} autoComplete="off" onChange={this.changeAttach}/> 张
              <ResetAttach config={config} orgcode={orgcode} voucherCode={this.state.voucherCode} attachlist={attachlist} onChange={this.resetAttachList}/>
              <ResetRemark remark={remark} ID={config.uuid + 'remark'} onChange={(val) => this.setState({status: 'change', remark: val})}/>
            </div>
          </div> : null}
          {type === 'createTemp' || type === 'checkTemp' ? <div className="pre-temp-wrap">
            <div className="temp-text">
              <span>模板名称:</span><Input value={title} placeholder="模板名称" autoComplete="off" onChange={(e) => this.setState({title: e.target.value})}/>
            </div>
            <div className="temp-text">
              <span>模板类型:</span>
              <Select value={tempTypeClass} dropdownClassName="mk-vcode-dropdown" onChange={(val, option) => this.setState({tempTypeClass: val, tempTypeName: option.props.children})}>
                {tempTypes.map(option =>
                  <Select.Option key={option.data_code} value={option.data_code}>{option.data_name}</Select.Option>
                )}
              </Select>
            </div>
            <div className="temp-action">
              <Button className="save-temp header-btn" onClick={() => this.triggerTempsave()}>保存</Button>
              <Button className="close-temp header-btn" onClick={this.triggerclose}>关闭</Button>
            </div>
          </div> : null}
          <VoucherTable config={config} loading={loading} data={data} onChange={this.dataChange}/>
            <VoucherTable config={config} loading={loading} data={data} onChange={this.dataChange}/>
          </div>
          <div className="user" style={{paddingLeft: `${config.wrap.space || 0}px`}}>制单人:{username}</div>
        </div>
        {type === 'createVoucher' || type === 'checkVoucher' ? <div className="user">制单人:{username}</div> : null}
      </div>
    )
      )
    } else if (type === 'createVoucher') {
      return (
        <div className="menu-voucher-wrap" style={config.style}>
          <div className="voucher-header" style={{padding: `10px ${config.wrap.space || 0}px`}}>
            <Button disabled={status === 'empty'} onClick={() => this.triggersave('add')}>保存并新增</Button>
            <Button disabled={status === 'empty' || status === 'saved'} onClick={() => this.triggersave()}>保存</Button>
            <Button disabled={status !== 'saved'} onClick={this.triggerPrint}>打印</Button>
            <Dropdown overlay={<div className="mk-voucher-dropdown-wrap">
              <SaveAsTemp tempTypes={tempTypes} onChange={this.triggerTempsave}/>
              <div className="split"></div>
              <LoadFromTemp tempTypes={tempTypes} onChange={this.triggerTempLoad}/>
            </div>} trigger={['hover']} placement="bottomCenter">
              <Button>更多</Button>
            </Dropdown>
          </div>
          <div className="voucher-body" style={{padding: `0px ${config.wrap.space || 0}px`}}>
            <div className="pre-wrap">
              <div className="voucher-code">
                <Select value={charType} dropdownClassName="mk-vcode-dropdown" onChange={(val, option) => this.setState({status: 'change', charType: val, charName: option.props.charname, charInt: option.props.charint})}>
                  {typeOptions.map(option =>
                    <Select.Option key={option.id} value={option.voucher_class} charname={option.voucher_char} charint={option.voucher_char_int}>{option.voucher_char}</Select.Option>
                  )}
                </Select>
                <InputNumber precision={0} min={1} value={charInt} autoComplete="off" onChange={(val) => this.setState({status: 'change', charInt: val})}/> 号
              </div>
              <div className="voucher-date">
                日期:<DatePicker value={vouDate} onChange={this.changeVouDate}/>
              </div>
              <div className="voucher-text">
                <Input value={title} placeholder="凭证文本" autoComplete="off" onChange={(e) => this.setState({status: 'change', title: e.target.value})}/>
              </div>
              <div className="voucher-affix">
                附单据 <InputNumber precision={0} value={attachments || 0} autoComplete="off" onChange={this.changeAttach}/> 张
                <ResetAttach config={config} orgcode={orgcode} voucherCode={this.state.voucherCode} attachlist={attachlist} onChange={this.resetAttachList}/>
                <ResetRemark remark={remark} ID={config.uuid + 'remark'} onChange={(val) => this.setState({status: 'change', remark: val})}/>
              </div>
            </div>
            <VoucherTable config={config} loading={loading} data={data} onChange={this.dataChange}/>
          </div>
          <div className="user" style={{paddingLeft: `${config.wrap.space || 0}px`}}>制单人:{username}</div>
        </div>
      )
    } else {
      return (
        <div className="menu-voucher-wrap" style={config.style}>
          <div className="voucher-body" style={{padding: `0px ${config.wrap.space || 0}px`}}>
            <div className="pre-temp-wrap">
              <div className="temp-text">
                <span>模板名称:</span><Input value={title} placeholder="模板名称" autoComplete="off" onChange={(e) => this.setState({title: e.target.value})}/>
              </div>
              <div className="temp-text">
                <span>模板类型:</span>
                <Select value={tempTypeClass} dropdownClassName="mk-vcode-dropdown" onChange={(val, option) => this.setState({tempTypeClass: val, tempTypeName: option.props.children})}>
                  {tempTypes.map(option =>
                    <Select.Option key={option.data_code} value={option.data_code}>{option.data_name}</Select.Option>
                  )}
                </Select>
              </div>
              <div className="temp-action">
                <Button onClick={() => this.triggerTempsave()}>保存</Button>
                <Button onClick={this.triggerclose}>关闭</Button>
              </div>
            </div>
            <VoucherTable config={config} loading={loading} data={data} onChange={this.dataChange}/>
          </div>
        </div>
      )
    }
  }
}
src/tabviews/custom/components/module/voucher/index.scss
@@ -14,13 +14,31 @@
    padding: 10px;
    border-bottom: 1px solid #eeeeee;
    .header-btn {
    .ant-btn {
      height: 28px;
      min-width: 80px;
      margin-right: 10px;
      background-color: #ffffff;
      border-color: #d8d8d8;
      color: rgba(0, 0, 0, 0.65);
    }
    .ant-btn[disabled] {
      background-color: #dadada!important;
      border-color: #dadada!important;
      color: #ffffff!important;
    }
    .ant-btn:not([disabled]):hover {
      background-color: var(--mk-sys-color);
      border-color: var(--mk-sys-color);
      color: #ffffff;
    }
  }
  .voucher-body {
    .voucher-number {
      display: inline-block;
      white-space: nowrap;
      margin-right: 15px;
    }
    .voucher-code {
      display: inline-block;
      width: 160px;
@@ -52,6 +70,10 @@
    }
    .pre-wrap {
      padding: 10px 0px;
      .ant-input.ant-input-disabled, .ant-input-number.ant-input-number-disabled, .ant-select.ant-select-disabled {
        color: rgba(0, 0, 0, 0.65);
      }
    }
    .voucher-date {
      display: inline-block;
@@ -61,7 +83,7 @@
    }
    .voucher-text {
      display: inline-block;
      width: calc(56% - 350px);
      width: calc(68% - 545px);
      margin-left: 12px;
    }
    .voucher-affix {
@@ -91,42 +113,14 @@
        .ant-btn {
          margin-left: 15px;
        }
        .ant-btn:hover {
          color: var(--mk-sys-color);
          border-color: var(--mk-sys-color);
        }
      }
    }
  }
  .add-background {
    background: #26C281;
    border-color: #26C281;
    color: #ffffff;
  }
  .print-background {
    background-color: #8E44AD;
    border-color: #8E44AD;
    color: #ffffff;
  }
  .out-background {
    background-color: rgb(50, 197, 210);
    border-color: rgb(50, 197, 210);
    color: #ffffff;
  }
  .save-temp {
    background-color: var(--mk-sys-color);
    border-color: var(--mk-sys-color);
    color: #ffffff;
  }
  .close-temp {
    background-color: #ffffff;
    border-color: #f5222d;
    color: #f5222d;
  }
  .system-background {
    background: var(--mk-sys-color);
    border-color: var(--mk-sys-color);
    color: #ffffff;
  }
  .user {
    padding: 15px 30px 0px;
  }
@@ -145,10 +139,19 @@
  border: 1px solid #d8d8d8;
  border-radius: 4px;
  min-height: 66px;
  overflow: hidden;
  .ant-btn {
    display: block;
    width: 100%;
    border: none;
    color: rgba(0, 0, 0, 0.65);
    border-radius: 0;
  }
  .ant-btn:not([disabled]):hover {
    background-color: var(--mk-sys-color);
    border-color: var(--mk-sys-color);
    color: #ffffff;
  }
  .split {
    width: 100%;
src/tabviews/custom/components/module/voucher/resetAttach/index.jsx
@@ -231,6 +231,7 @@
  }
  render() {
    const { disabled } = this.props
    const { visible, upVisible, docVisible, files, list, documents, loading } = this.state
    return (
@@ -248,8 +249,8 @@
          destroyOnClose
        >
          {loading ? <Spin /> : null}
          <Button type="link" className="attach-type-btn" disabled={loading} onClick={() => this.setState({upVisible: true})}>上传新文件</Button>
          <Button type="link" className="attach-type-btn" disabled={loading} onClick={() => this.setState({docVisible: true, selectDocs: []})}>从会计电子档案选择</Button>
          <Button type="link" className="attach-type-btn" disabled={loading || disabled} onClick={() => this.setState({upVisible: true})}>上传新文件</Button>
          <Button type="link" className="attach-type-btn" disabled={loading || disabled} onClick={() => this.setState({docVisible: true, selectDocs: []})}>从会计电子档案选择</Button>
          <div className="attach-selected-list">
            {list.map(item => {
              return <div className="attach-item" key={item.id}>
@@ -259,7 +260,7 @@
                  <div>{item.data_name ? item.data_name + ' / ' : ''}{item.attachments_title}</div>
                </div>
                <div>
                  <DeleteOutlined onClick={() => this.deleteFile(item.id)}/>
                  {!disabled ? <DeleteOutlined onClick={() => this.deleteFile(item.id)}/> : null}
                </div>
              </div>
            })}
src/tabviews/custom/components/module/voucher/resetRemark/index.jsx
@@ -37,7 +37,7 @@
  }
  render() {
    const { ID } = this.props
    const { ID, disabled } = this.props
    const { visible, remark } = this.state
    return (
@@ -52,7 +52,7 @@
          onCancel={() => { this.setState({ visible: false })}}
          destroyOnClose
        >
          <TextArea id={ID} defaultValue={remark} rows={6}/>
          <TextArea id={ID} disabled={disabled} defaultValue={remark} rows={6}/>
        </Modal>
      </>
    )
src/tabviews/custom/components/module/voucher/voucherTable/index.jsx
@@ -712,7 +712,9 @@
      if (record.type === 'total') {
        children = <div className="content-wrap" style={{lineHeight: '60px'}}>合计: {val}</div>
        colSpan = 2
      } else {
      } else if (record.$disabled) {
        children = <div className="content-wrap">{val}</div>
      } else  {
        extra = <PlusOutlined onClick={this.plusLine}/>
        if (editing) {
@@ -817,6 +819,17 @@
                  <span><InputNumber precision={4} className="inner-input" id={col.uuid + record.uuid + 'price'} defaultValue={record.net_unitprice || 0} onChange={(val) => this.onChange(val)} onPressEnter={this.pricePress} onBlur={this.priceBlur}/></span>
                </span>
              </div>
            } else if (record.$disabled) {
              countNode = <div className="count-wrap">
                <span style={{marginRight: '5px'}}>
                  <span>数量:</span>
                  <span>{record.fcc_count || 0}</span>
                </span>
                <span>
                  <span>单价:</span>
                  <span>{record.net_unitprice || 0}</span>
                </span>
              </div>
            } else {
              countNode = <div className="count-wrap">
                <span style={{marginRight: '5px'}} onClick={this.editCount}>
@@ -891,6 +904,21 @@
                  <span><InputNumber precision={2} className="inner-input" id={col.uuid + record.uuid + 'origin'} defaultValue={record.foreign_amount || 0} onChange={(val) => this.onChange(val)} onPressEnter={this.originPress} onBlur={this.originBlur}/></span>
                </span>
              </div>
            } else if (record.$disabled) {
              currencyNode = <div className="count-wrap">
                <span style={{marginRight: '5px'}}>
                  <span>货币:</span>
                  <span>{record.exratename || ''}</span>
                </span>
                <span style={{marginRight: '5px'}}>
                  <span>汇率:</span>
                  <span>{record.unitratio || 1}</span>
                </span>
                <span>
                  <span>原币:</span>
                  <span>{record.foreign_amount || 0}</span>
                </span>
              </div>
            } else {
              currencyNode = <div className="count-wrap">
                <span style={{marginRight: '5px'}} onClick={this.editCurrency}>
@@ -909,11 +937,19 @@
            }
          }
          children = <div className="content-wrap" onClick={this.focus}>
            {val}
            {countNode}
            {currencyNode}
          </div>
          if (record.$disabled) {
            children = <div className="content-wrap">
              {val}
              {countNode}
              {currencyNode}
            </div>
          } else {
            children = <div className="content-wrap" onClick={this.focus}>
              {val}
              {countNode}
              {currencyNode}
            </div>
          }
        }
      }
    } else if (col.field === 'debit') {
@@ -930,13 +966,23 @@
          }
          vals = (val * 100).toFixed(0).split('').reverse()
        }
        children = <div className={'money-uint' + (down ? ' down' : '')} onClick={this.focus}>
          <span>{vals[10] || ''}</span> <span>{vals[9] || ''}</span> <span>{vals[8] || ''}</span> <span>{vals[7] || ''}</span> <span>{vals[6] || ''}</span> <span>{vals[5] || ''}</span>
          <span>{vals[4] || ''}</span> <span>{vals[3] || ''}</span> <span>{vals[2] || ''}</span> <span>{vals[1] || ''}</span> <span className="last">{vals[0] || ''}</span>
        </div>
        if (record.$disabled) {
          children = <div className={'money-uint' + (down ? ' down' : '')}>
            <span>{vals[10] || ''}</span> <span>{vals[9] || ''}</span> <span>{vals[8] || ''}</span> <span>{vals[7] || ''}</span> <span>{vals[6] || ''}</span> <span>{vals[5] || ''}</span>
            <span>{vals[4] || ''}</span> <span>{vals[3] || ''}</span> <span>{vals[2] || ''}</span> <span>{vals[1] || ''}</span> <span className="last">{vals[0] || ''}</span>
          </div>
        } else {
          children = <div className={'money-uint' + (down ? ' down' : '')} onClick={this.focus}>
            <span>{vals[10] || ''}</span> <span>{vals[9] || ''}</span> <span>{vals[8] || ''}</span> <span>{vals[7] || ''}</span> <span>{vals[6] || ''}</span> <span>{vals[5] || ''}</span>
            <span>{vals[4] || ''}</span> <span>{vals[3] || ''}</span> <span>{vals[2] || ''}</span> <span>{vals[1] || ''}</span> <span className="last">{vals[0] || ''}</span>
          </div>
        }
      }
    } else if (col.field === 'credit') {
      extra = <CloseOutlined onClick={this.delVoucher}/>
      if (record.type !== 'total' && !record.$disabled) {
        extra = <CloseOutlined onClick={this.delVoucher}/>
      }
      if (editing) {
        children = <InputNumber id={col.uuid + record.uuid} precision={2} defaultValue={record.credit} onChange={(val) => this.onChange(val)} onPressEnter={this.enterPress} onBlur={this.onBlur}/>
@@ -951,10 +997,18 @@
          }
          vals = (val * 100).toFixed(0).split('').reverse()
        }
        children = <div className={'money-uint' + (down ? ' down' : '')} onClick={this.focus}>
        <span>{vals[10] || ''}</span> <span>{vals[9] || ''}</span> <span>{vals[8] || ''}</span> <span>{vals[7] || ''}</span> <span>{vals[6] || ''}</span> <span>{vals[5] || ''}</span>
        <span>{vals[4] || ''}</span> <span>{vals[3] || ''}</span> <span>{vals[2] || ''}</span> <span>{vals[1] || ''}</span> <span className="last">{vals[0] || ''}</span>
      </div>
        if (record.$disabled) {
          children = <div className={'money-uint' + (down ? ' down' : '')}>
            <span>{vals[10] || ''}</span> <span>{vals[9] || ''}</span> <span>{vals[8] || ''}</span> <span>{vals[7] || ''}</span> <span>{vals[6] || ''}</span> <span>{vals[5] || ''}</span>
            <span>{vals[4] || ''}</span> <span>{vals[3] || ''}</span> <span>{vals[2] || ''}</span> <span>{vals[1] || ''}</span> <span className="last">{vals[0] || ''}</span>
          </div>
        } else {
          children = <div className={'money-uint' + (down ? ' down' : '')} onClick={this.focus}>
            <span>{vals[10] || ''}</span> <span>{vals[9] || ''}</span> <span>{vals[8] || ''}</span> <span>{vals[7] || ''}</span> <span>{vals[6] || ''}</span> <span>{vals[5] || ''}</span>
            <span>{vals[4] || ''}</span> <span>{vals[3] || ''}</span> <span>{vals[2] || ''}</span> <span>{vals[1] || ''}</span> <span className="last">{vals[0] || ''}</span>
          </div>
        }
      }
    }
@@ -966,17 +1020,17 @@
class VoucherTable extends Component {
  static propTpyes = {
    config: PropTypes.object,        // 菜单Id
    data: PropTypes.any,             // 表格数据
    loading: PropTypes.bool,         // 表格加载中
    onChange: PropTypes.func,        // 表格变动
    config: PropTypes.object,
    data: PropTypes.any,
    loading: PropTypes.bool,
    onChange: PropTypes.func
  }
  state = {
    data: [],
    edData: [],
    tableId: '',          // 表格ID
    columns: null,        // 显示列
    tableId: '',
    columns: null,
  }
  UNSAFE_componentWillMount () {
@@ -1105,9 +1159,11 @@
      return item
    })
    let disabled = _data[0] && _data[0].$disabled ? true : false
    if (_data.length < 4) {
      for (let i = _data.length - 1; i < 4; i++) {
        _data.push({uuid: Utils.getguid(), index: i + 1, subject_voucher_text: '', subject_code: '', subject_name: '', debit: '', credit: ''})
        _data.push({uuid: Utils.getguid(), $disabled: disabled, index: i + 1, subject_voucher_text: '', subject_code: '', subject_name: '', debit: '', credit: ''})
      }
    }
    return _data
src/tabviews/custom/components/module/voucher/voucherTable/index.scss
@@ -10,9 +10,8 @@
    position: absolute;
    bottom: 10px;
  }
  >.ant-table-wrapper {
  .ant-table-wrapper {
    position: relative;
    z-index: 1;
  }
  .ant-table {
    color: inherit;
src/tabviews/custom/components/share/normalTable/index.jsx
@@ -20,6 +20,63 @@
  '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 triggerLink = (e, item, record) => {
  e.stopPropagation()
  if (item.linkThdMenu) {
    let __param = {
      $BID: record.$$uuid
    }
    if (item.field) {
      __param.$searchkey = item.field.toLowerCase()
      __param.$searchval = record[item.field] || ''
    }
    if (item.linkThdMenu.urlFields) {
      let lower = {}
      Object.keys(record).forEach(key => {
        lower[key.toLowerCase()] = record[key]
      })
      item.linkThdMenu.urlFields.split(',').forEach(field => {
        __param[field] = lower[field.toLowerCase()] || ''
      })
    } else if (item.linkfields && item.linkfields.length > 0) {
      item.linkfields.forEach(field => {
        __param[field] = record[field] || ''
      })
    }
    let tabmenu = item.linkThdMenu
    tabmenu.param = __param
    MKEmitter.emit('modifyTabs', tabmenu, true)
  } else if (item.linkurl) {
    let src = item.linkurl
    let con = '?'
    if (/\?/ig.test(src)) {
      con = '&'
    }
    if (item.linkfields && item.linkfields.length > 0) {
      item.linkfields.forEach(field => {
        if (field.toLowerCase() === 'id') return
        con += `${field}=${record[field] || ''}&`
      })
    }
    src = src + `${con}id=${record.$$uuid}&appkey=${window.GLOB.appkey}&userid=${sessionStorage.getItem('UserID')}&LoginUID=${sessionStorage.getItem('LoginUID') || ''}`
    window.open(src)
  }
}
class BodyRow extends React.Component {
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props.data), fromJS(nextProps.data)) || this.props.className !== nextProps.className
@@ -107,7 +164,7 @@
  }
  render() {
    let { col, config, record, className, style, triggerLink, ...resProps } = this.props
    let { col, config, record, className, style, ...resProps } = this.props
    if (!col) return (<td {...resProps} className={className} style={style}/>)
@@ -169,10 +226,7 @@
      resProps.children = content
      if (!record.$disabled && (col.linkThdMenu || col.linkurl)) {
        style = style || {}
        style.cursor = 'pointer'
        return (<td {...resProps} className={className} onDoubleClick={() => triggerLink(col, record)} style={style}/>)
        return (<td {...resProps} className={className + ' clickable'} onClick={(e) => triggerLink(e, col, record)} style={style}/>)
      }
    } else if (col.type === 'number') {
      let content = ''
@@ -202,6 +256,7 @@
        }
  
        if (col.format === 'thdSeparator') {
          content = content + ''
          content = content.replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, '$&,')
        }
  
@@ -243,10 +298,7 @@
      resProps.children = content
      if (!record.$disabled && (col.linkThdMenu || col.linkurl)) {
        style = style || {}
        style.cursor = 'pointer'
        return (<td {...resProps} className={className} onDoubleClick={() => triggerLink(col, record)} style={style}/>)
        return (<td {...resProps} className={className + ' clickable'} onClick={(e) => triggerLink(e, col, record)} style={style}/>)
      }
    } else if (col.type === 'picture') {
      let photos = ''
@@ -528,7 +580,6 @@
                record,
                col: item,
                config: item.type === 'custom' ? {setting, columns: fields} : null,
                triggerLink: this.triggerLink
              })
            }
          }
@@ -802,48 +853,6 @@
  
      MKEmitter.emit('resetSelectLine', MenuID, '', '')
      MKEmitter.emit('syncBalconyData', MenuID, [], false)
    }
  }
  // 字段透视
  triggerLink = (item, record) => {
    let __param = {
      $searchkey: item.field,
      $searchval: record[item.field] || '',
      $BID: record.$$uuid
    }
    if (item.linkfields && item.linkfields.length > 0) {
      item.linkfields.forEach(field => {
        __param[field] = record[field] || ''
      })
    }
    if (item.linkThdMenu) {
      let tabmenu = item.linkThdMenu
      tabmenu.param = __param
      MKEmitter.emit('modifyTabs', tabmenu, true)
    } else if (item.linkurl) {
      let src = item.linkurl
      let con = '?'
      if (/\?/ig.test(src)) {
        con = '&'
      }
      if (item.linkfields && item.linkfields.length > 0) {
        item.linkfields.forEach(field => {
          if (field.toLowerCase() === 'id') return
          con += `${field}=${record[field] || ''}&`
        })
      }
      src = src + `${con}id=${record.$$uuid}&appkey=${window.GLOB.appkey}&userid=${sessionStorage.getItem('UserID')}&LoginUID=${sessionStorage.getItem('LoginUID') || ''}`
      window.open(src)
    }
  }
@@ -1159,7 +1168,7 @@
    }
    return (
      <div className={`normal-custom-table ${setting.tableHeader || ''} ${height ? 'fixed-height' : ''} ${setting.mode || ''} table-vertical-${setting.vertical || 'middle'} table-col-${columns.length} ${fixed}`} style={style}>
      <div className={`normal-custom-table ${setting.tableHeader || ''} ${setting.parity === 'true' ? 'mk-parity' : ''} ${height ? 'fixed-height' : ''} ${setting.mode || ''} table-vertical-${setting.vertical || 'middle'} table-col-${columns.length} ${fixed}`} style={style}>
        {(setting.tableType === 'radio' || setting.tableType === 'checkbox') && data && data.length > 0 ?
          <Switch title="收起" className="main-pickup" checkedChildren="开" unCheckedChildren="关" checked={pickup} onChange={this.pickupChange} /> : null
        }
src/tabviews/custom/components/share/normalTable/index.scss
@@ -24,13 +24,20 @@
    position: absolute;
    bottom: 10px;
  }
  >.ant-table-wrapper {
  .ant-table-wrapper {
    position: relative;
    z-index: 1;
  }
  .ant-table {
    color: inherit;
    font-size: inherit;
  }
  .clickable {
    color: var(--mk-sys-color);
    cursor: pointer;
  }
  .clickable:hover {
    text-decoration: underline;
  }
  .mk-disabled {
@@ -201,6 +208,11 @@
    color: var(--mk-table-color);
  }
}
.normal-custom-table.mk-parity {
  .ant-table-tbody tr:nth-child(even) {
    background: #fafafa;
  }
}
.normal-custom-table:not(.ghost) {
  .ant-table-small > .ant-table-content .ant-table-thead > tr > th {
    background-color: #fafafa!important;
src/tabviews/custom/components/share/normalheader/index.scss
@@ -12,6 +12,7 @@
    text-decoration: inherit;
    font-weight: inherit;
    font-style: inherit;
    font-family: inherit;
    float: left;
    line-height: inherit;
    margin-left: 10px;
@@ -38,6 +39,9 @@
        .ant-input-search-button {
          height: 28px;
        }
        .ant-form-explain {
          display: none;
        }
      }
    }
  }
src/tabviews/custom/components/share/tabtransfer/index.jsx
@@ -294,9 +294,7 @@
        )
      } else if (item.type === 'group' && item.subtype === 'normalgroup') {
        return (
          <Col span={item.width} style={style} key={item.uuid}>
            <NormalGroup config={item}/>
          </Col>
          <NormalGroup config={item} style={style} key={item.uuid}/>
        )
      } else if (item.type === 'form' && item.subtype === 'simpleform') {
        return (
src/tabviews/custom/components/table/base-table/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { notification, Modal } from 'antd'
import { notification } from 'antd'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
@@ -143,6 +143,7 @@
    this.requestId = config.uuid + new Date().getTime()
    let result = await Api.genericInterface(param, setting.js_script, '', this.requestId)
    if (result.status) {
      if (result.$requestId && this.requestId !== result.$requestId) return
@@ -228,19 +229,7 @@
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
@@ -250,18 +239,7 @@
        MKEmitter.emit('autoMaticError', config.MenuID)
      }
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
@@ -383,11 +361,8 @@
      this.setState({
        loading: false
      })
      notification.error({
        top: 92,
        message: result.message,
        duration: 10
      })
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/table/edit-table/index.jsx
@@ -1,7 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { notification, Modal } from 'antd'
import Api from '@/api'
import Utils from '@/utils/utils.js'
@@ -355,36 +354,13 @@
      MKEmitter.emit('transferData' + setting.tableId, data)
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
@@ -487,11 +463,8 @@
      this.setState({
        loading: false
      })
      notification.error({
        top: 92,
        message: result.message,
        duration: 10
      })
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/table/edit-table/normalTable/index.jsx
@@ -866,6 +866,7 @@
        }
  
        if (col.format === 'thdSeparator') {
          content = content + ''
          content = content.replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, '$&,')
        }
  
@@ -1190,6 +1191,7 @@
          }
    
          if (col.format === 'thdSeparator') {
            content = content + ''
            content = content.replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, '$&,')
          }
    
@@ -2652,7 +2654,7 @@
        {setting.hasSubmit && edData.length > 0 ? <div className="edit-custom-table-btn-wrap" style={submit.wrapStyle}>
          <Button style={submit.style} onClick={() => setTimeout(() => {this.submit()}, 10)} loading={loading} className="submit-table" type="link">提交</Button>
        </div> : null}
        <div className={`edit-custom-table ${setting.tableHeader || ''} ${height ? 'fixed-height' : ''} ${setting.mode || ''} table-vertical-${setting.vertical || ''} mk-edit-${setting.editType || 'simple'}`} style={style}>
        <div className={`edit-custom-table ${setting.tableHeader || ''} ${setting.parity === 'true' ? 'mk-parity' : ''} ${height ? 'fixed-height' : ''} ${setting.mode || ''} table-vertical-${setting.vertical || ''} mk-edit-${setting.editType || 'simple'}`} style={style}>
          <Table
            rowKey="$$uuid"
            components={components}
src/tabviews/custom/components/table/edit-table/normalTable/index.scss
@@ -7,9 +7,8 @@
  --mk-table-font-size: 14px;
  --mk-table-font-weight: normal;
  >.ant-table-wrapper {
  .ant-table-wrapper {
    position: relative;
    // z-index: 1;
  }
  .ant-table-placeholder {
    border-top-color: var(--mk-table-border-color);
@@ -269,6 +268,11 @@
  //   }
  // }
}
.edit-custom-table.mk-parity {
  .ant-table-tbody tr:nth-child(even) {
    background: #fafafa;
  }
}
.edit-custom-table.mk-edit-simple {
  table tbody tr td {
    min-height: 32px;
src/tabviews/custom/components/table/normal-table/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { notification, Collapse, Modal } from 'antd'
import { Collapse } from 'antd'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
@@ -43,6 +43,7 @@
  }
  loaded = false
  supModules = []
  /**
   * @description 初始化处理
@@ -529,37 +530,14 @@
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
@@ -685,11 +663,8 @@
      this.setState({
        loading: false
      })
      notification.error({
        top: 92,
        message: result.message,
        duration: 10
      })
      UtilsDM.queryFail(result)
    }
  }
@@ -780,20 +755,41 @@
  }
  resetParentParam = (MenuID, id, data) => {
    const { setting } = this.state
    const { config, setting } = this.state
    if (!setting.supModule || setting.supModule !== MenuID) return
    if (config.supNodes) {
      if (!config.supNodes.includes(MenuID)) return
      this.supModules = this.supModules.filter(item => item.key !== MenuID)
      let bid = ''
      let _data = null
    if (id !== this.state.BID || id !== '') {
      this.setState({
        pageIndex: 1,
        BID: id,
        BData: data
      }, () => {
        if (!setting.checkBid) {
      if (id) {
        this.supModules.push({key: MenuID, value: id, data})
      }
      if (this.supModules.length > 0) {
        bid = this.supModules[this.supModules.length - 1].value
        _data = this.supModules[this.supModules.length - 1].data
      }
      if (bid !== this.state.BID || bid !== '') {
        this.setState({ BID: bid, BData: _data, pageIndex: 1 }, () => {
          this.loadmaindata(true, 'true')
        }
      })
        })
      }
    } else {
      if (!setting.supModule || setting.supModule !== MenuID) return
      if (id !== this.state.BID || id !== '') {
        this.setState({
          pageIndex: 1,
          BID: id,
          BData: data
        }, () => {
          if (!setting.checkBid) {
            this.loadmaindata(true, 'true')
          }
        })
      }
    }
  }
@@ -808,16 +804,41 @@
    if (config.uuid !== menuId) return
    if (position === 'line' || position === 'line_grid') {
      if (lines && lines.length === 1) {
        this.loadLinedata(lines[0].$$uuid, position)
    if (config.supNodes) {
      if (position === 'mainline' || position === 'popclose') {
        let supNode = this.supModules[this.supModules.length - 1]
        config.supNodes.forEach((item, i) => {
          setTimeout(() => {
            if (supNode && supNode.key === item) {
              MKEmitter.emit('reloadData', item, supNode.value)
            } else {
              MKEmitter.emit('reloadData', item)
            }
          }, i * 10)
        })
      } else {
        if (position === 'line' || position === 'line_grid') {
          if (lines && lines.length === 1) {
            this.loadLinedata(lines[0].$$uuid, position)
          } else {
            this.reloadtable(btn, id)
          }
        } else {
          this.reloadtable(btn, id)
        }
      }
    } else {
      if (position === 'line' || position === 'line_grid') {
        if (lines && lines.length === 1) {
          this.loadLinedata(lines[0].$$uuid, position)
        } else {
          this.reloadtable(btn, id)
        }
      } else if ((position === 'mainline' || position === 'popclose') && config.setting.supModule && BID) { // 刷新源组件时,附带刷新上级行与当前组件
        MKEmitter.emit('reloadData', config.setting.supModule, BID)
      } else {
        this.reloadtable(btn, id)
      }
    } else if ((position === 'mainline' || position === 'popclose') && config.setting.supModule && BID) { // 刷新源组件时,附带刷新上级行与当前组件
      MKEmitter.emit('reloadData', config.setting.supModule, BID)
    } else {
      this.reloadtable(btn, id)
    }
  }
src/tabviews/custom/components/timeline/normal-timeline/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification, Timeline, Empty, Modal } from 'antd'
import { Spin, notification, Timeline, Empty } from 'antd'
import Api from '@/api'
import MkIcon from '@/components/mk-icon'
@@ -377,37 +377,14 @@
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
src/tabviews/custom/components/tree/antd-tree/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, Empty, notification, Input, Tree, Modal, Dropdown } from 'antd'
import { Spin, Empty, Input, Tree, Dropdown } from 'antd'
import { FolderOpenOutlined, FolderOutlined, FileOutlined, MoreOutlined } from '@ant-design/icons'
import Api from '@/api'
@@ -31,6 +31,7 @@
    treeNodes: null,           // 列表数据集
    expandedKeys: [],          // 展开的树节点
    selectedKeys: [],          // 选中的树节点
    lineActions: [],
    selected: false            // 选中首行
  }
@@ -71,13 +72,28 @@
      }
    }
    let lineActions = []
    _config.action = _config.action || []
    _config.action = _config.action.filter(item => {
      if (item.Ot === 'requiredSgl') {
        lineActions.push(item)
        return false
      }
      return true
    })
    _config.wrap.contentHeight = config.wrap.title || config.wrap.searchable === 'true' ? 'calc(100% - 45px)' : '100%'
    if (_config.wrap.lineHeight) {
      _config.style['--mk-tree-line-height'] = _config.wrap.lineHeight + 'px'
    }
    this.setState({
      selected: _config.wrap.selected === 'true',
      config: _config,
      data: _data,
      BID: BID || ''
      BID: BID || '',
      lineActions
    })
  }
@@ -254,7 +270,7 @@
   * @param {*} position   // 刷新位置
   * @param {*} btn        // 执行的按钮
   */
  refreshByButtonResult = (menuId, position, btn) => {
  refreshByButtonResult = (menuId, position) => {
    const { config, BID } = this.state
    if (config.uuid !== menuId) return
@@ -321,37 +337,14 @@
        }
      }
      if (result.message) {
        if (result.ErrCode === 'Y') {
          Modal.success({
            title: result.message
          })
        } else if (result.ErrCode === 'S') {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      }
      UtilsDM.querySuccess(result)
    } else {
      this.setState({
        loading: false
      })
      this.timer && this.timer.stop()
      
      if (!result.message) return
      if (result.ErrCode === 'N') {
        Modal.error({
          title: result.message,
        })
      } else if (result.ErrCode !== '-2') {
        notification.error({
          top: 92,
          message: result.message,
          duration: 10
        })
      }
      UtilsDM.queryFail(result)
    }
  }
@@ -376,9 +369,9 @@
    let hasSelectKey = false
    data.forEach(item => {
      let pval = item[config.wrap.parentField]
      let val = item[config.wrap.valueField]
      let uuid = item[config.setting.primaryKey] || ''
      let pval = item[config.wrap.parentField] + ''
      let val = item[config.wrap.valueField] + ''
      let uuid = item[config.setting.primaryKey] + ''
      if (!val || logMap.has(val)) return
@@ -528,30 +521,45 @@
    })
  }
  renderActionTreeNodes = (nodes) => {
  renderActionTreeNodes = (nodes, actShow) => {
    return nodes.map(item => {
      let title = <>
        {item.$title}
        <Dropdown overlay={
          <div className="mk-tree-dropdown-wrap" onClick={(e) => e.stopPropagation()}>
            <MainAction
              BID={this.state.BID}
              BData={this.state.BData}
              setting={this.state.config.setting}
              actions={this.state.config.action}
              columns={this.state.config.columns}
              selectedData={[{...item, children: ''}]}
            />
          </div>
        } placement="bottomCenter" trigger={['hover']}>
          <MoreOutlined onClick={(e) => e.stopPropagation()}/>
        </Dropdown>
      </>
      let title = null
      if (actShow === 'line') {
        title = <>
          {item.$title}
          <MainAction
            BID={this.state.BID}
            BData={this.state.BData}
            setting={this.state.config.setting}
            actions={this.state.lineActions}
            columns={this.state.config.columns}
            selectedData={[{...item, children: ''}]}
          />
        </>
      } else {
        title = <>
          {item.$title}
          <Dropdown overlay={
            <div className="mk-tree-dropdown-wrap" onClick={(e) => e.stopPropagation()}>
              <MainAction
                BID={this.state.BID}
                BData={this.state.BData}
                setting={this.state.config.setting}
                actions={this.state.lineActions}
                columns={this.state.config.columns}
                selectedData={[{...item, children: ''}]}
              />
            </div>
          } placement="bottomCenter" trigger={['hover']}>
            <MoreOutlined onClick={(e) => e.stopPropagation()}/>
          </Dropdown>
        </>
      }
      if (item.children) {
        return (
          <TreeNode icon={<span><FolderOpenOutlined /><FolderOutlined /></span>} title={title} key={item.$key} dataRef={item}>
            {this.renderActionTreeNodes(item.children)}
            {this.renderActionTreeNodes(item.children, actShow)}
          </TreeNode>
        )
      }
@@ -614,9 +622,7 @@
  }
  render() {
    const { config, loading, treeNodes, expandedKeys, selectedKeys } = this.state
    let extra = config.action && config.action.length > 0
    const { BID, BData, config, loading, treeNodes, expandedKeys, selectedKeys, lineActions } = this.state
    return (
      <div className="custom-tree-box" id={'anchor' + config.uuid} style={config.style}>
@@ -630,18 +636,26 @@
          <span className={'title ' + (config.wrap.searchable !== 'true' ? 'search-unable' : '')}>{config.wrap.title}</span>
          {config.wrap.searchable === 'true' ? <Search allowClear onSearch={this.treeFilter} /> : null}
        </div> : null}
        {config.action.length ? <MainAction
          BID={BID}
          setting={config.setting}
          actions={config.action}
          BData={BData}
          columns={config.columns}
          selectedData={[]}
        /> : null}
        {treeNodes && treeNodes.length > 0 ? <div className="tree-box" style={{height: config.wrap.contentHeight}}>
          <Tree
            blockNode
            onSelect={this.selectTreeNode}
            expandedKeys={expandedKeys}
            selectedKeys={selectedKeys}
            onRightClick={!extra ? this.changeExpandedAllKeys : null}
            onRightClick={!lineActions.length ? this.changeExpandedAllKeys : null}
            onExpand={this.changeExpandedKeys}
            showIcon={config.wrap.showIcon === 'true'}
            showLine={config.wrap.showLine === 'true'}
          >
            {!extra ? this.renderTreeNodes(treeNodes) : this.renderActionTreeNodes(treeNodes)}
            {!lineActions.length ? this.renderTreeNodes(treeNodes) : this.renderActionTreeNodes(treeNodes, config.wrap.actShow)}
          </Tree>
        </div> : null}
        {treeNodes && treeNodes.length === 0 ? <Empty description={false}/> : null}
src/tabviews/custom/components/tree/antd-tree/index.scss
@@ -5,6 +5,7 @@
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 50px;
  --mk-tree-line-height: 24px;
  .tree-header {
    position: relative;
@@ -18,6 +19,7 @@
      text-decoration: inherit;
      font-weight: inherit;
      font-style: inherit;
      font-family: inherit;
      float: left;
      line-height: 45px;
      margin-left: 10px;
@@ -44,6 +46,39 @@
      }
    }
  }
  .ant-tree-title {
    .button-list.toolbar-button {
      position: absolute;
      right: 0px;
      top: 0px;
      padding: 0px;
      min-height: 24px;
      height: var(--mk-tree-line-height);
      button {
        min-width: 20px;
        margin-bottom: 0px !important;
        min-height: 24px;
        border-width: 0px;
        vertical-align: middle;
      }
    }
  }
  .ant-tree-node-content-wrapper {
    .button-list.toolbar-button {
      opacity: 0;
      transition: all 0.3s;
    }
  }
  .ant-tree-node-content-wrapper:hover {
    .button-list.toolbar-button {
      opacity: 1;
    }
  }
  .ant-tree li .ant-tree-node-content-wrapper, .ant-tree li span.ant-tree-switcher, .ant-tree li span.ant-tree-iconEle {
    height: var(--mk-tree-line-height);
    line-height: var(--mk-tree-line-height);
  }
  .tree-box {
    overflow: auto;
    padding-bottom: 10px;
@@ -61,6 +96,8 @@
        right: 0px;
        padding: 5px 10px;
        color: var(--mk-sys-color);
        height: var(--mk-tree-line-height);
        line-height: var(--mk-tree-line-height);
      }
    }
  }
@@ -117,21 +154,22 @@
.mk-tree-dropdown-wrap {
  box-shadow: 0 0 2px #bcbcbc;
  background: #ffffff;
  min-width: 85px;
  min-width: 100px;
  .button-list.toolbar-button {
    padding: 0px;
    min-height: 36px;
    button {
      display: block;
      margin: 0!important;
      width: 100%;
      border-radius: 0px;
      padding-left: 15px!important;
      .anticon {
        display: none;
      }
      .anticon + span {
        margin-left: 0px;
      }
      border: 0px !important;
      // color: rgba(0, 0, 0, 0.85)!important;
      // background: transparent !important;
      min-height: 36px;
    }
    button:not(:last-child) {
      border-bottom: 1px solid #d8d8d8 !important;
    }
  }
}
src/tabviews/custom/index.jsx
@@ -41,6 +41,7 @@
const AntvX6 = asyncComponent(() => import('./components/chart/antv-X6'))
const Voucher = asyncComponent(() => import('./components/module/voucher'))
const Account = asyncComponent(() => import('./components/module/account'))
const Invoice = asyncComponent(() => import('./components/module/invoice'))
const Iframe = asyncComponent(() => import('./components/iframe'))
const Calendar = asyncComponent(() => import('./components/calendar'))
const DebugTable = asyncComponent(() => import('@/tabviews/debugtable'))
@@ -265,18 +266,6 @@
      // 字段透视
      config.components.forEach(component => {
        if (component.type !== 'search') return
        if (param.$searchkey) {
          component.search = component.search.map(item => {
            if (['text', 'select', 'link', 'checkcard'].includes(item.type) && param.$searchkey === item.field) {
              item.initval = param.$searchval
            }
            return item
          })
          component.$searches = Utils.initMainSearch(component.search)
        }
        window.GLOB.SearchBox.set(MenuID, component.$searches)
@@ -565,8 +554,14 @@
        item.type = 'card'
      }
      if (item.wrap && item.wrap.supType === 'multi') { // 数据卡多上级组件
        item.setting.supModule = item.supNodes[0].componentId
      if (item.wrap && item.wrap.supType === 'multi') { // 数据卡、table多上级组件
        item.supNodes = item.supNodes.map(node => node.componentId)
        if (item.supNodes[0]) {
          item.setting.supModule = item.supNodes[0]
        } else {
          item.supNodes = null
          item.setting.supModule = ''
        }
      } else if (item.setting && item.setting.supModule && typeof(item.setting.supModule) !== 'string') {
        let pid = item.setting.supModule.pop()
        if (pid && pid !== 'empty') {
@@ -574,13 +569,33 @@
        } else {
          item.setting.supModule = ''
        }
      } else if (item.type === 'search') {
        if (item.wrap.supModule) {
          item.wrap.supModule = item.wrap.supModule.pop()
        }
      }
      // 搜索条件初始化
      if (item.search) {
        Utils.initSearchVal(item)
        if (urlparam.$searchkey) {
          item.search.forEach(cell => {
            if (urlparam.$searchkey === cell.field.toLowerCase() && ['text', 'select', 'link', 'checkcard'].includes(cell.type)) {
              cell.initval = urlparam.$searchval
            }
          })
        }
        item.$searches = Utils.initMainSearch(item.search)
        if (item.type === 'search' && item.wrap.supModule) {
          if (!item.checkBid) {
            item.wrap.supModule = ''
          } else {
            window.GLOB.SearchBox.set(item.$searchId + 'checkBid', true)
          }
        }
      }
      let pass = skip
@@ -602,7 +617,7 @@
            cell = this.getPrinter(cell, item.uuid)
          }
          return pass || permAction[cell.uuid]
          return pass || permAction[cell.uuid] || cell.permission === 'false'
        })
      }
@@ -644,7 +659,7 @@
                    cell = this.getPrinter(cell, item.uuid)
                  }
                  return pass || permAction[cell.uuid]
                  return pass || permAction[cell.uuid] || cell.permission === 'false'
                } else {
                  cell = this.resetElement(cell)
                }
@@ -707,7 +722,7 @@
              cell = this.resetElement(cell)
            }
            return cell.eleType !== 'button' || pass || permAction[cell.uuid]
            return cell.eleType !== 'button' || pass || permAction[cell.uuid] || cell.permission === 'false'
          })
          if (card.setting.click === 'menus') {
@@ -749,7 +764,7 @@
              cell = this.resetElement(cell)
            }
            return cell.eleType !== 'button' || pass || permAction[cell.uuid]
            return cell.eleType !== 'button' || pass || permAction[cell.uuid] || cell.permission === 'false'
          })
        })
      } else if (item.type === 'balcony') {
@@ -773,7 +788,7 @@
            cell = this.resetElement(cell)
          }
          return cell.eleType !== 'button' || pass || permAction[cell.uuid]
          return cell.eleType !== 'button' || pass || permAction[cell.uuid] || cell.permission === 'false'
        })
      } else if (item.type === 'form') {
        item.subcards = item.subcards.map(group => {
@@ -945,7 +960,7 @@
    if (cell.syncComponentId) {
      if (cell.syncComponentId === item.setting.supModule) {
        cell.syncComponentId = ''
        if (cell.execSuccess === 'line' || cell.execSuccess === 'grid') {
        if (['line', 'grid', 'line_grid'].includes(cell.execSuccess)) {
          cell.execSuccess = 'mainline'
        }
      } else if (cell.syncComponentId === 'multiComponent') {
@@ -953,8 +968,17 @@
          return m.syncComId.pop() || ''
        })
        if (item.setting.supModule && ids.includes(item.setting.supModule)) {
          if (cell.execSuccess === 'line' || cell.execSuccess === 'grid') {
        if (item.supNodes) {
          item.supNodes.forEach(node => {
            if (!ids.includes(node)) return
            if (['line', 'grid', 'line_grid'].includes(cell.execSuccess)) {
              cell.execSuccess = 'mainline'
            }
            ids = ids.filter(id => id !== node)
          })
        } else if (item.setting.supModule && ids.includes(item.setting.supModule)) {
          if (['line', 'grid', 'line_grid'].includes(cell.execSuccess)) {
            cell.execSuccess = 'mainline'
          }
          ids = ids.filter(id => id !== item.setting.supModule)
@@ -980,6 +1004,17 @@
    if (cell.marks && cell.marks.length === 0) {
      cell.marks = null
    }
    if (cell.anchors && cell.anchors.length === 0) {
      cell.anchors = null
    }
    if (cell.linkmenu && cell.linkmenu.length > 0) {
      let menu_id = cell.linkmenu.pop()
      cell.linkThdMenu = window.GLOB.mkThdMenus.get(menu_id) || ''
      if (!cell.linkThdMenu) {
        cell.link = ''
      }
    }
    if (['text', 'number', 'formula'].includes(cell.eleType)) {
@@ -1071,8 +1106,14 @@
      if (component.setting.useMSearch) {
        if (!window.GLOB.SearchBox.has(component.$searchId)) {
          component.setting.useMSearch = false
        } else if (window.GLOB.SearchBox.has(component.$searchId + 'required')) {
          component.$s_req = true
        } else {
          if (window.GLOB.SearchBox.has(component.$searchId + 'required')) {
            component.$s_req = true
          }
          if (window.GLOB.SearchBox.has(component.$searchId + 'checkBid')) {
            component.checkBid = true
            component.setting.checkBid = true
          }
        }
      }
@@ -1613,9 +1654,7 @@
        )
      } else if (item.type === 'group' && item.subtype === 'normalgroup') {
        return (
          <Col span={item.width} style={style} key={item.uuid}>
            <NormalGroup config={item}/>
          </Col>
          <NormalGroup config={item} style={style} key={item.uuid}/>
        )
      } else if (item.type === 'editor') {
        return (
@@ -1671,6 +1710,12 @@
            <Account config={item}/>
          </Col>
        )
      } else if (item.type === 'module' && item.subtype === 'invoice') {
        return (
          <Col span={item.width} style={style} key={item.uuid}>
            <Invoice config={item}/>
          </Col>
        )
      } else if (item.type === 'iframe') {
        return (
          <Col span={item.width} style={style} key={item.uuid}>
src/tabviews/custom/popview/index.jsx
@@ -135,18 +135,6 @@
    config.components.forEach(component => {
      if (component.type !== 'search') return
      if (param.$searchkey) {
        component.search = component.search.map(item => {
          if (['text', 'select', 'link', 'checkcard'].includes(item.type) && param.$searchkey === item.field) {
            item.initval = param.$searchval
          }
          return item
        })
        component.$searches = Utils.initMainSearch(component.search)
      }
      window.GLOB.SearchBox.set(Tab.uuid, component.$searches)
      if (component.$s_req) {
@@ -326,8 +314,14 @@
        }
      }
      if (item.wrap && item.wrap.supType === 'multi') { // 数据卡多上级组件
        item.setting.supModule = item.supNodes[0].componentId
      if (item.wrap && item.wrap.supType === 'multi') { // 数据卡、table多上级组件
        item.supNodes = item.supNodes.map(node => node.componentId)
        if (item.supNodes[0]) {
          item.setting.supModule = item.supNodes[0]
        } else {
          item.supNodes = null
          item.setting.supModule = ''
        }
      } else if (item.setting && item.setting.supModule && typeof(item.setting.supModule) !== 'string') {
        let pid = item.setting.supModule.pop()
        if (pid && pid !== 'empty') {
@@ -663,7 +657,7 @@
    if (cell.syncComponentId) {
      if (cell.syncComponentId === item.setting.supModule) {
        cell.syncComponentId = ''
        if (cell.execSuccess === 'line' || cell.execSuccess === 'grid') {
        if (['line', 'grid', 'line_grid'].includes(cell.execSuccess)) {
          cell.execSuccess = 'mainline'
        }
      } else if (cell.syncComponentId === 'multiComponent') {
@@ -671,8 +665,17 @@
          return m.syncComId.pop() || ''
        })
        if (item.setting.supModule && ids.includes(item.setting.supModule)) {
          if (cell.execSuccess === 'line' || cell.execSuccess === 'grid') {
        if (item.supNodes) {
          item.supNodes.forEach(node => {
            if (!ids.includes(node)) return
            if (['line', 'grid', 'line_grid'].includes(cell.execSuccess)) {
              cell.execSuccess = 'mainline'
            }
            ids = ids.filter(id => id !== node)
          })
        } else if (item.setting.supModule && ids.includes(item.setting.supModule)) {
          if (['line', 'grid', 'line_grid'].includes(cell.execSuccess)) {
            cell.execSuccess = 'mainline'
          }
          ids = ids.filter(id => id !== item.setting.supModule)
@@ -698,6 +701,17 @@
    if (cell.marks && cell.marks.length === 0) {
      cell.marks = null
    }
    if (cell.anchors && cell.anchors.length === 0) {
      cell.anchors = null
    }
    if (cell.linkmenu && cell.linkmenu.length > 0) {
      let menu_id = cell.linkmenu.pop()
      cell.linkThdMenu = window.GLOB.mkThdMenus.get(menu_id) || ''
      if (!cell.linkThdMenu) {
        cell.link = ''
      }
    }
    if (['text', 'number', 'formula'].includes(cell.eleType)) {
@@ -1121,9 +1135,7 @@
        )
      } else if (item.type === 'group' && item.subtype === 'normalgroup') {
        return (
          <Col span={item.width} style={style} key={item.uuid}>
            <NormalGroup config={item}/>
          </Col>
          <NormalGroup config={item} style={style} key={item.uuid}/>
        )
      } else if (item.type === 'editor') {
        return (
src/tabviews/debugtable/index.jsx
@@ -44,6 +44,8 @@
    let param = UtilsDM.getQueryDataParams(setting, [], 'sort', 1, 50, '')
    delete param.s_version_up
    let result = await Api.genericInterface(param)
    if (result.status) {
src/tabviews/subtable/index.jsx
@@ -221,6 +221,7 @@
      let _actions = []     // 工具栏按钮
      let _operations = []  // 操作列按钮(存在时)
      let colors = { primary: '#1890ff', yellow: '#c49f47', orange: 'orange', danger: '#ff4d4f', green: '#26C281', dgreen: '#32c5d2', purple: '#8E44AD', cyan: '#13c2c2', gray: '#E7E7EF', default: 'rgba(0, 0, 0, 0.65)' }
      config.action.forEach(item => {
        item.logLabel = Tab.label + '-' + item.label // 用于sPC_TableData_InUpDe记录操作按钮
@@ -270,10 +271,25 @@
          }
        }
        
        item.show = 'button'
        let _c = item.class ? item.class.replace('border-', '') : ''
        let color = colors[_c] || '#1890ff'
        if (item.position === 'toolbar') {
          item.$toolbtn = true
          if (item.class === 'default') {
            item.style = {color: 'rgba(0, 0, 0, 0.65)', backgroundColor: '#fff', borderColor: '#d9d9d9', marginRight: '15px'}
          } else if (item.class.indexOf('border') > -1) {
            item.style = {color: color, backgroundColor: '#fff', borderColor: color, marginRight: '15px'}
          } else {
            item.style = {color: item.class === 'gray' ? 'rgba(0, 0, 0, 0.65)' : '#fff', backgroundColor: color, borderColor: color, marginRight: '15px'}
          }
          _actions.push(item)
        } else if (item.position === 'grid') {
          item.style = {color: color, backgroundColor: 'transparent', borderColor: 'transparent'}
          _operations.push(item)
        }
      })
@@ -464,6 +480,8 @@
    let _orderBy = orderBy || setting.order
    let param = UtilsDM.getQueryDataParams(setting, searches, _orderBy, pageIndex, pageSize, BID)
    delete param.s_version_up
    let result = await Api.genericInterface(param)
    this.getStatFieldsValue(searches)
@@ -550,6 +568,8 @@
    let _orderBy = orderBy || setting.order
    let param = UtilsDM.getQueryDataParams(setting, searches, _orderBy, pageIndex, pageSize, BID, id)
    delete param.s_version_up
    let result = await Api.genericInterface(param)
    if (result.status) {
      let data = fromJS(this.state.data).toJS()
src/tabviews/subtabtable/index.jsx
@@ -192,6 +192,7 @@
      let _actions = []     // 工具栏按钮
      let _operations = []  // 操作列按钮(存在时)
      let colors = { primary: '#1890ff', yellow: '#c49f47', orange: 'orange', danger: '#ff4d4f', green: '#26C281', dgreen: '#32c5d2', purple: '#8E44AD', cyan: '#13c2c2', gray: '#E7E7EF', default: 'rgba(0, 0, 0, 0.65)' }
      config.action.forEach(item => {
        item.logLabel = Tab.label + '-' + item.label // 用于sPC_TableData_InUpDe记录操作按钮
@@ -228,10 +229,25 @@
          }
        }
        item.show = 'button'
        let _c = item.class ? item.class.replace('border-', '') : ''
        let color = colors[_c] || '#1890ff'
        if (item.position === 'toolbar') {
          item.$toolbtn = true
          if (item.class === 'default') {
            item.style = {color: 'rgba(0, 0, 0, 0.65)', backgroundColor: '#fff', borderColor: '#d9d9d9', marginRight: '15px'}
          } else if (item.class.indexOf('border') > -1) {
            item.style = {color: color, backgroundColor: '#fff', borderColor: color, marginRight: '15px'}
          } else {
            item.style = {color: item.class === 'gray' ? 'rgba(0, 0, 0, 0.65)' : '#fff', backgroundColor: color, borderColor: color, marginRight: '15px'}
          }
          _actions.push(item)
        } else if (item.position === 'grid') {
          item.style = {color: color, backgroundColor: 'transparent', borderColor: 'transparent'}
          _operations.push(item)
        }
      })
@@ -398,6 +414,8 @@
    let _orderBy = orderBy || setting.order
    let param = UtilsDM.getQueryDataParams(setting, searches, _orderBy, pageIndex, pageSize, BID)
    delete param.s_version_up
    let result = await Api.genericInterface(param)
    this.getStatFieldsValue(searches)
@@ -523,6 +541,8 @@
    let _orderBy = orderBy || setting.order
    let param = UtilsDM.getQueryDataParams(setting, searches, _orderBy, pageIndex, pageSize, BID, id)
    delete param.s_version_up
    let result = await Api.genericInterface(param)
    if (result.status) {
      let data = fromJS(this.state.data).toJS()
src/tabviews/treepage/index.jsx
@@ -265,6 +265,8 @@
    let param = UtilsDM.getQueryDataParams(setting, [], setting.order, '', '', BID)
    delete param.s_version_up
    let result = await Api.genericInterface(param)
    if (result.status) {
      let parentNodes = []
src/tabviews/zshare/actionList/changeuserbutton/index.jsx
@@ -223,38 +223,23 @@
    if (hidden) return null
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || ''
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = !loading ? <MkIcon type={btn.icon} /> : null
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <span>{!loading && btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{btn.label}</span>
    }
    return (
      <Button
        type={type}
        type="link"
        title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
        loading={loading}
        disabled={disabled}
        style={btn.style}
        icon={icon}
        className={className}
        className={btn.$toolbtn ? (btn.hover || '') : ''}
        onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
      >{label}</Button>
    )
src/tabviews/zshare/actionList/editLine/index.jsx
@@ -109,37 +109,22 @@
    if (hidden) return null
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || ''
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <MkIcon type={btn.icon} />
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <span>{btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{btn.label}</span>
    }
    return (
      <Button
        type={type}
        type="link"
        title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
        disabled={disabled}
        style={btn.style}
        icon={icon}
        className={className}
        className={btn.$toolbtn ? (btn.hover || '') : ''}
        onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
      >{label}</Button>
    )
src/tabviews/zshare/actionList/excelInbutton/index.jsx
@@ -408,6 +408,9 @@
        param.username = sessionStorage.getItem('User_Name') || ''
        param.fullname = sessionStorage.getItem('Full_Name') || ''
      }
      if (btn.dataM === 'true') {
        param.dataM = sessionStorage.getItem('dataM') === 'true' ? 'Y' : ''
      }
      Api.genericInterface(param).then((res) => {
        if (res.status) {
@@ -534,39 +537,24 @@
    if (hidden) return null
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || 'upload'
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = !loading ? <MkIcon type={btn.icon} /> : null
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <span>{!loading && btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{btn.label}</span>
    }
    return <>
      <Button
        type={type}
        type="link"
        id={'button' + btn.uuid}
        title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
        loading={loading}
        disabled={disabled}
        style={btn.style}
        icon={icon}
        className={className}
        className={btn.$toolbtn ? (btn.hover || '') : ''}
        onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
      >{label}</Button>
      <ExcelIn btn={btn} triggerExcelIn={() => this.setState({ loading: true })} returndata={this.getexceldata} ref="excelIn" />
src/tabviews/zshare/actionList/exceloutbutton/index.jsx
@@ -184,6 +184,9 @@
          param.username = sessionStorage.getItem('User_Name') || ''
          param.fullname = sessionStorage.getItem('Full_Name') || ''
        }
        if (btn.dataM === 'true') {
          param.dataM = sessionStorage.getItem('dataM') === 'true' ? 'Y' : ''
        }
        Api.genericInterface(param).then(result => {
          if (result.status) {
@@ -387,6 +390,9 @@
        param.username = sessionStorage.getItem('User_Name') || ''
        param.fullname = sessionStorage.getItem('Full_Name') || ''
      }
      if (btn.dataM === 'true') {
        param.dataM = sessionStorage.getItem('dataM') === 'true' ? 'Y' : ''
      }
    } else if (btn.intertype === 'outer' && !btn.innerFunc) { // 使用外部函数
      param = this.getExcelCustomParam(viewParam.orderBy, viewParam.search, true, pageIndex, pageSize)
@@ -443,23 +449,70 @@
  /**
   * @description Excel 生成
   */
  exportExcel = (data, ErrCode, msg, search) => {
  exportExcel = (data = [], ErrCode, msg, search) => {
    const { btn } = this.props
    
    let imgCol = false
    let columns = btn.verify.columns.map(col => {
    let merge = false
    let styles = []
    let letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    let columns = btn.verify.columns.map((col, index) => {
      if (col.type === 'image') {
        imgCol = true
      }
      if (btn.verify.merge === 'true' && /.+-.+/.test(col.Text)) {
        merge = true
      }
      if (col.type === 'number' && (col.decimal || col.decimal === 0) ) {
        col.round = Math.pow(10, col.decimal)
      let i = Math.floor(index / 26)
      let s = letters[i - 1] || ''
      col.name = s + letters[index % 26]
      if (col.type === 'number') {
        if (col.decimal || col.decimal === 0) {
          col.round = Math.pow(10, col.decimal)
          if (col.format) {
            let dec = Array(col.decimal).fill(0).join('')
            dec = dec ? '.' + dec : ''
            if (col.format === 'thdSeparator') {
              col.z = '#,##0' + dec
            } else if (col.format === 'thdSepPm') {
              col.z = '#,##0' + dec + ';'
              col.z = col.z + '[Red]-' + col.z
            } else if (col.format === 'percent') {
              let _dec = ''
              if (col.decimal > 2) {
                Array(col.decimal - 2).fill(0).join('')
                _dec = _dec ? '.' + _dec : ''
              }
              col.z = '0' + _dec + '%'
            }
          }
        }
      } else if (col.type === 'text') {
        if (col.wrapText === 'true') {
          col.s = {alignment: { wrapText: true }}
        }
        if (col.textFormat) {
          if (col.textFormat === 'YYYY-MM-DD') {
            col.z = 'yyyy-mm-dd;@'
          } else if (col.textFormat === 'YYYY-MM-DD HH:mm:ss') {
            col.z = 'yyyy-mm-dd hh:mm:ss'
          }
        }
      }
      if (col.z || col.s) {
        styles.push(col)
      }
      return col
    })
    if (data && data[0]) {
    if (data[0]) {
      let errors = []
      columns.forEach(col => {
        if (col.output === 'false') return
@@ -508,7 +561,7 @@
        let table = []
        data && data.forEach((item, index) => {
        data.forEach((item, index) => {
          let _row = {}
  
          item.$Index = index + 1 + ''
@@ -516,21 +569,48 @@
          columns.forEach((col, i) => {
            let val = item[col.Column]
            if (col.output === 'false') {
              if (col.type === 'number') {
              if (col.type === 'number' && col.noValue !== 'false') {
                val = 0
              } else {
                val = ''
              }
            } else if (col.type === 'number' && typeof(val) === 'number') {
              if (col.abs === 'true') {
                val = Math.abs(val)
            } else if (col.type === 'number') {
              if (val && typeof(val) === 'string' && !isNaN(val)) {
                val = +val
              }
              if (col.round) {
                val = Math.round(val * col.round) / col.round
                // val = val.toFixed(col.decimal)
              if (typeof(val) === 'number') {
                if (col.abs === 'true') {
                  val = Math.abs(val)
                }
                if (col.round) {
                  val = Math.round(val * col.round) / col.round
                }
                if (col.noValue === 'false' && val === 0) {
                  val = ''
                }
              }
              if (col.noValue === 'false' && val === 0) {
            } else if (col.type === 'text') {
              val = val + ''
              if (col.textFormat) {
                if (col.textFormat === 'YYYY-MM-DD' && /^[1-9]\d{3}(-|\/)(0[1-9]|1[0-2])(-|\/)(0[1-9]|[1-2][0-9]|3[0-1])/.test(val)) {
                  val = `${val.substr(0, 4)}-${val.substr(5, 2)}-${val.substr(8, 2)}`
                } else if (col.textFormat === 'YYYY-MM-DD HH:mm:ss' && /^[1-9]\d{3}(-|\/)(0[1-9]|1[0-2])(-|\/)(0[1-9]|[1-2][0-9]|3[0-1]).([0-1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]/.test(val)) {
                  val = `${val.substr(0, 4)}-${val.substr(5, 2)}-${val.substr(8, 2)} ${val.substr(11, 2)}:${val.substr(14, 2)}:${val.substr(17, 2)}`
                }
              }
              if (col.noValue === 'false' && val < '1949-10-02') {
                val = ''
              }
            }
            if (val !== '') {
              if (col.prefix) {
                val = col.prefix + val
              }
              if (col.postfix) {
                val = val + col.postfix
              }
            }
@@ -544,16 +624,6 @@
        this.execSuccess({ErrCode: ErrCode || 'S', message: msg || '导出成功!'})
      } else {
        let letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
        let cols = []
        for (let index = 0; index < columns.length; index++) {
          let i = Math.floor(index / 26)
          let s = letters[i - 1] || ''
          cols.push(s + letters[index % 26])
        }
        let table = []
        let _header = []
        let _topRow = {}
@@ -561,16 +631,16 @@
        let requires = []
        let merges = []
        columns.forEach((col, i) => {
        columns.forEach(col => {
          _header.push(col.Column)
          _topRow[col.Column] = col.Text
          colwidth.push({wch: col.Width || 20})
          if (col.required === 'true') {
            requires.push(i)
            requires.push(col.name)
          }
        })
        if (btn.verify.merge === 'true') {
        if (merge) {
          let fLine = {}
          let sLine = {}
          let sign = ''
@@ -582,16 +652,16 @@
              sLine[col.Column] = _name
              if (sign === _sign) {
                merges[merges.length - 1] = merges[merges.length - 1].split(':')[0] + `:${cols[i]}1`
                merges[merges.length - 1] = merges[merges.length - 1].split(':')[0] + `:${col.name}1`
              } else {
                merges.push(`${cols[i]}1:${cols[i]}2`)
                merges.push(`${col.name}1:${col.name}2`)
                sign = _sign
              }
            } else {
              fLine[col.Column] = col.Text
              sLine[col.Column] = col.Text
              sign = ''
              merges.push(`${cols[i]}1:${cols[i]}2`)
              merges.push(`${col.name}1:${col.name}2`)
            }
          })
@@ -601,7 +671,7 @@
          table.push(_topRow)
        }
  
        data && data.forEach((item, index) => {
        data.forEach((item, index) => {
          let _row = {}
  
          item.$Index = index + 1 + ''
@@ -610,21 +680,48 @@
            let val = item[col.Column]
            if (col.output === 'false') {
              if (col.type === 'number') {
              if (col.type === 'number' && col.noValue !== 'false') {
                val = 0
              } else {
                val = ''
              }
            } else if (col.type === 'number' && typeof(val) === 'number') {
              if (col.abs === 'true') {
                val = Math.abs(val)
            } else if (col.type === 'number') {
              if (val && typeof(val) === 'string' && !isNaN(val)) {
                val = +val
              }
              if (col.round) {
                val = Math.round(val * col.round) / col.round
                // val = val.toFixed(col.decimal)
              if (typeof(val) === 'number') {
                if (col.abs === 'true') {
                  val = Math.abs(val)
                }
                if (col.round) {
                  val = Math.round(val * col.round) / col.round
                }
                if (col.noValue === 'false' && val === 0) {
                  val = ''
                }
              }
              if (col.noValue === 'false' && val === 0) {
            } else if (col.type === 'text') {
              val = val + ''
              if (col.textFormat) {
                if (col.textFormat === 'YYYY-MM-DD' && /^[1-9]\d{3}(-|\/)(0[1-9]|1[0-2])(-|\/)(0[1-9]|[1-2][0-9]|3[0-1])/.test(val)) {
                  val = `${val.substr(0, 4)}-${val.substr(5, 2)}-${val.substr(8, 2)}`
                } else if (col.textFormat === 'YYYY-MM-DD HH:mm:ss' && /^[1-9]\d{3}(-|\/)(0[1-9]|1[0-2])(-|\/)(0[1-9]|[1-2][0-9]|3[0-1]).([0-1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]/.test(val)) {
                  val = `${val.substr(0, 4)}-${val.substr(5, 2)}-${val.substr(8, 2)} ${val.substr(11, 2)}:${val.substr(14, 2)}:${val.substr(17, 2)}`
                }
              }
              if (col.noValue === 'false' && val < '1949-10-02') {
                val = ''
              }
            }
            if (val !== '') {
              if (col.prefix) {
                val = col.prefix + val
              }
              if (col.postfix) {
                val = val + col.postfix
              }
            }
@@ -644,49 +741,46 @@
        if (requires.length) {
          requires.forEach(col => {
            if (cols[col]) {
              ws[cols[col] + '1'].s = {font: { color: { rgb: 'F5222D' } }}
            }
            ws[col + '1'].s = {font: { color: { rgb: 'F5222D' } }}
          })
        }
        if (merges.length) {
        if (merge) {
          ws['!merges'] = []
          merges.forEach(item => {
            ws['!merges'].push(XLSX.utils.decode_range(item))
          })
          cols.forEach(col => {
            if (ws[col + '1'].s) {
              ws[col + '1'].s = {font: { color: { rgb: 'F5222D' } }, alignment: { horizontal: 'center', vertical: 'center' }}
            } else {
              ws[col + '1'].s = {alignment: { horizontal: 'center', vertical: 'center' }}
            }
            ws[col + '2'].s = {alignment: { horizontal: 'center', vertical: 'center' }}
          columns.forEach(col => {
            ws[col.name + '1'].s = ws[col.name + '1'].s || {}
            ws[col.name + '1'].s.alignment = { horizontal: 'center', vertical: 'center' }
            ws[col.name + '2'].s = {alignment: { horizontal: 'center', vertical: 'center' }}
          })
        }
        if (btn.verify.wrapText === 'true' && data) {
          let lines = data.length + 1
          let start = 2
          if (btn.verify.merge === 'true') {
            lines = data.length + 2
          }
          for (let n = 0; n < cols.length; n++) {
            for (let m = start; m <= lines; m++) {
              if (ws[cols[n] + m] && !ws[cols[n] + m].s) {
                ws[cols[n] + m].s = {alignment: { wrapText: true }}
        // ws['A3'].s = {font: { sz: 10 , bold: true }, alignment: { horizontal: 'center', vertical: 'center' }, border: {top: {style: 'thin', color: '000000'}, left: {style: 'thin', color: '000000'}, bottom: {style: 'thin', color: '000000'}, right: {style: 'thin', color: '000000'}}};
        // ws['A3'].z = '#,##0.00';
        // ws['A3'].z = '#,##0.00;[Red]-#,##0.00;';
        // ws["A1"].s = {fill: { bgColor: { rgb: "FFFFAA"  }}, font: { color: { rgb: "1890FF" } }}
        if (data.length && styles.length) {
          for (let n = table.length - data.length + 1; n <= table.length; n++) {
            styles.forEach(col => {
              if (col.z) {
                ws[col.name + n].z = col.z
              }
            }
              if (col.s) {
                ws[col.name + n].s = col.s
              }
            })
          }
        }
        // ws["A1"].s = {fill: { bgColor: { rgb: "FFFFAA"  }}, font: { color: { rgb: "1890FF" } }}
        const wb = XLSX.utils.book_new()
        XLSX.utils.book_append_sheet(wb, ws, btn.verify.sheet || 'Sheet1')
  
        XLSX.writeFile(wb, `${btn.$menuName || ''}${moment().format('YYYYMMDDHHmmss')}.xlsx`)
        XLSX.writeFile(wb, `${btn.verify.excelName || btn.$menuName || ''}${moment().format('YYYYMMDDHHmmss')}.xlsx`)
  
        this.execSuccess({ErrCode: ErrCode || 'S', message: msg || '导出成功!'})
      }
@@ -784,9 +878,9 @@
      _setting.arr_field = []
      btn.verify.columns.forEach(col => {
        if (col.Column && col.Column !== '$Index') {
          _setting.arr_field.push(col.Column)
        }
        if (col.output === 'false' || !col.Column || col.Column === '$Index') return
        _setting.arr_field.push(col.Column)
      })
      _setting.arr_field = _setting.arr_field.join(',')
      _setting.execute = btn.verify.defaultSql !== 'false'
@@ -986,39 +1080,24 @@
    if (hidden) return null
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || 'download'
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = !loading ? <MkIcon type={btn.icon} /> : null
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <span>{!loading && btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{btn.label}</span>
    }
    return (
      <Button
        type={type}
        type="link"
        id={'button' + btn.uuid}
        title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
        loading={loading}
        disabled={disabled}
        style={btn.style || null}
        icon={icon}
        className={className}
        className={btn.$toolbtn ? (btn.hover || '') : ''}
        onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
      >{label}</Button>
    )
src/tabviews/zshare/actionList/exportPdf/index.jsx
@@ -109,42 +109,28 @@
    const { btn } = this.props
    const { loading } = this.state
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    let style = {...btn.style}
    if (loading) {
      style.opacity = 0
    }
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    let label = ''
    if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || ''
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <MkIcon type={btn.icon} />
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <span>{btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{btn.label}</span>
    }
    
    return (
      <Button
        type={type}
        type="link"
        title={btn.show === 'icon' ? btn.label : ''}
        style={style}
        icon={icon}
        className={className}
        className={btn.$toolbtn ? (btn.hover || '') : ''}
        onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
      >{label}</Button>
    )
src/tabviews/zshare/actionList/funcMegvii/index.jsx
@@ -514,43 +514,26 @@
    if (hidden) return null
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || ''
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = !loading ? <MkIcon type={btn.icon} /> : null
    } else if (btn.$toolbtn) {
      label = <span>{!loading && btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{loadingNumber && !loadingTotal ? `(${loadingNumber})` : ''}{btn.label}</span>
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
    }
    if (loadingNumber && !loadingTotal && btn.$toolbtn && (!btn.show || btn.show === 'button')) {
      label = (loadingNumber && !loadingTotal ? `(${loadingNumber})` : '') + btn.label
      label = <span>{!loading && btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{btn.label}</span>
    }
    return (
      <>
        <Button
          type={type}
          type="link"
          title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
          loading={loading}
          disabled={disabled}
          style={btn.style}
          icon={icon}
          className={className}
          className={btn.$toolbtn ? (btn.hover || '') : ''}
          onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
        >{label}</Button>
        {this.getModels()}
src/tabviews/zshare/actionList/funczip/index.jsx
@@ -409,42 +409,25 @@
    if (hidden) return null
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || ''
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = !loading ? <MkIcon type={btn.icon} /> : null
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <span>{!loading && btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{btn.label}</span>
    }
    
    return (
      <>
        <Button
          type={type}
          title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
          loading={loading}
          disabled={disabled}
          style={btn.style}
          icon={icon}
          className={className}
          onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
        >{label}</Button>
      </>
      <Button
        type="link"
        title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
        loading={loading}
        disabled={disabled}
        style={btn.style}
        className={btn.$toolbtn ? (btn.hover || '') : ''}
        onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
      >{label}</Button>
    )
  }
}
src/tabviews/zshare/actionList/index.scss
@@ -17,6 +17,19 @@
    padding: 0 15px;
  }
  .ant-btn.mk-btn-hover-bg:not([disabled]):hover {
    opacity: 1!important;
    color: #ffffff!important;
    border-color: var(--mk-sys-color)!important;
    background-color: var(--mk-sys-color)!important;
  }
  .ant-btn.mk-btn-hover-border:not([disabled]):hover {
    opacity: 1!important;
    color: var(--mk-sys-color)!important;
    border-color: var(--mk-sys-color)!important;
    background-color: #ffffff!important;
  }
  .loading-skeleton {
    background: -webkit-gradient(linear,left top,right top,color-stop(25%,#f5f5f5),color-stop(37%,#ffffff),color-stop(63%,#f5f5f5));
    background: linear-gradient(90deg,#f5f5f5 25%,#ffffff 37%,#f5f5f5 63%);
src/tabviews/zshare/actionList/newpagebutton/index.jsx
@@ -16,6 +16,7 @@
    btn: PropTypes.object,            // 按钮
    selectedData: PropTypes.any,      // 子表中选择数据
    disabled: PropTypes.any,          // 行按钮禁用
    name: PropTypes.any
  }
  state = {
@@ -159,18 +160,75 @@
    if (btn.pageTemplate === 'billprint') {
      _name = '单据打印'
      if (btn.Ot === 'required') {
        data.forEach(item => {
          let _id = item.$$uuid || ''
          let url = '#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify({ id: _id, tempId: btn.printTemp, pageId: btn.$MenuID || '', dataM: sessionStorage.getItem('dataM') })))
          window.open(url)
        })
      } else if (btn.Ot === 'requiredOnce') {
        Id = data.map(item => item.$$uuid).filter(Boolean).join(',')
      if (btn.preHandle === 'true' && btn.pre_func) {
        MKEmitter.emit('queryModuleParam', btn.$menuId, (res) => {
          let searches = {}
          res.search && res.search.forEach(item => {
            searches[item.key] = item.value
          })
        window.open('#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify({ id: Id, tempId: btn.printTemp, pageId: btn.$MenuID || '', dataM: sessionStorage.getItem('dataM') }))))
          if (btn.Ot === 'requiredOnce') {
            Id = data.map(item => item.$$uuid).filter(Boolean).join(',')
          }
          if (btn.Ot === 'required') {
            data.forEach(item => {
              let _param = { id: item.$$uuid || '', tempId: btn.printTemp, pageId: btn.$MenuID || '', dataM: sessionStorage.getItem('dataM')}
              try {
                // eslint-disable-next-line
                let func = new Function('btn', 'searches', 'data', 'param', 'systemType', btn.pre_func)
                _param = func(btn, searches, [item], _param, window.GLOB.systemType)
              } catch (e) {
                console.warn(e)
              }
              if (!_param || _param.error) {
                notification.warning({
                  top: 92,
                  message: _param ? _param.error : '未获取到打印参数,自定义脚本错误!',
                  duration: 5
                })
                return
              }
              window.open('#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify(_param))))
            })
          } else {
            let _param = { id: Id, tempId: btn.printTemp, pageId: btn.$MenuID || '', dataM: sessionStorage.getItem('dataM')}
            try {
              // eslint-disable-next-line
              let func = new Function('btn', 'searches', 'data', 'param', 'systemType', btn.pre_func)
              _param = func(btn, searches, data, _param, window.GLOB.systemType)
            } catch (e) {
              console.warn(e)
            }
            if (!_param || _param.error) {
              notification.warning({
                top: 92,
                message: _param ? _param.error : '未获取到打印参数,自定义脚本错误!',
                duration: 5
              })
              return
            }
            window.open('#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify(_param))))
          }
        })
      } else {
        window.open('#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify({ id: Id, tempId: btn.printTemp, pageId: btn.$MenuID || '', dataM: sessionStorage.getItem('dataM') }))))
        if (btn.Ot === 'required') {
          data.forEach(item => {
            let _id = item.$$uuid || ''
            let url = '#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify({ id: _id, tempId: btn.printTemp, pageId: btn.$MenuID || '', dataM: sessionStorage.getItem('dataM') })))
            window.open(url)
          })
        } else if (btn.Ot === 'requiredOnce') {
          Id = data.map(item => item.$$uuid).filter(Boolean).join(',')
          window.open('#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify({ id: Id, tempId: btn.printTemp, pageId: btn.$MenuID || '', dataM: sessionStorage.getItem('dataM') }))))
        } else {
          window.open('#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify({ id: Id, tempId: btn.printTemp, pageId: btn.$MenuID || '', dataM: sessionStorage.getItem('dataM') }))))
        }
      }
    } else if (btn.pageTemplate === 'billprintTemp') {
      let src = '#/menudesign/' + window.btoa(window.encodeURIComponent(JSON.stringify({ MenuType: 'billPrint', MenuId: Id, MenuNo: MenuNo, MenuName: name || '打印', Remark: Remark })))
@@ -250,44 +308,29 @@
  }
  render() {
    const { btn } = this.props
    const { btn, name } = this.props
    const { disabled, hidden } = this.state
    if (hidden) return null
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{name || btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || ''
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <MkIcon type={btn.icon} />
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <span>{btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{name || btn.label}</span>
    }
    return (
      <Button
        type={type}
        type="link"
        id={'button' + btn.uuid}
        title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
        style={btn.style || null}
        disabled={disabled}
        icon={icon}
        className={className}
        className={btn.$toolbtn ? (btn.hover || '') : ''}
        onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
      >{label}</Button>
    )
src/tabviews/zshare/actionList/normalbutton/index.jsx
@@ -28,6 +28,7 @@
    columns: PropTypes.any,           // 字段列
    setting: PropTypes.any,           // 页面通用设置
    disabled: PropTypes.any,          // 行按钮禁用
    name: PropTypes.any
  }
  state = {
@@ -755,6 +756,9 @@
        param.username = sessionStorage.getItem('User_Name') || ''
        param.fullname = sessionStorage.getItem('Full_Name') || ''
      }
      if (btn.dataM === 'true') {
        param.dataM = sessionStorage.getItem('dataM') === 'true' ? 'Y' : ''
      }
      
      let primaryId = ''
@@ -796,6 +800,9 @@
        if (btn.recordUser === 'true') {
          param.username = sessionStorage.getItem('User_Name') || ''
          param.fullname = sessionStorage.getItem('Full_Name') || ''
        }
        if (btn.dataM === 'true') {
          param.dataM = sessionStorage.getItem('dataM') === 'true' ? 'Y' : ''
        }
        let primaryId = setting.primaryKey ? cell[setting.primaryKey] || '' : ''
@@ -2344,9 +2351,9 @@
      tabId = btn.refreshTab[btn.refreshTab.length - 1]
    }
    if (btn.formCache === 'clear') { // 清除表单缓存
      window.GLOB.CacheMap = new Map()
    }
    // if (btn.formCache === 'clear') { // 清除表单缓存
    //   window.GLOB.CacheMap = new Map()
    // }
    if (tabId && btn.$MenuID === tabId) { // 刷新当前菜单时,停止其他操作
      MKEmitter.emit('reloadMenuView', tabId, 'table')
@@ -2474,9 +2481,38 @@
    if (!id) return
    setTimeout(() => {
      window.open('#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify({ id: id, tempId: btn.verify.printTempId, pageId: btn.$MenuID || '', dataM: sessionStorage.getItem('dataM') }))))
    }, 500)
    if (btn.verify.preHandle === 'true' && btn.verify.pre_func && /#position-print/.test(btn.verify.pre_func)) {
      MKEmitter.emit('queryModuleParam', btn.$menuId, (res) => {
        let searches = {}
        res.search && res.search.forEach(item => {
          searches[item.key] = item.value
        })
        let _param = { id: id || '', tempId: btn.verify.printTempId, pageId: btn.$MenuID || '', dataM: sessionStorage.getItem('dataM')}
        try {
          // eslint-disable-next-line
          let func = new Function('btn', 'searches', 'param', 'systemType', btn.verify.pre_func)
          _param = func(btn, searches, _param, window.GLOB.systemType)
        } catch (e) {
          console.warn(e)
        }
        if (!_param || _param.error) {
          notification.warning({
            top: 92,
            message: _param ? _param.error : '未获取到打印参数,自定义脚本错误!',
            duration: 5
          })
          return
        }
        window.open('#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify(_param))))
      })
    } else {
      setTimeout(() => {
        window.open('#/billprint/' + window.btoa(window.encodeURIComponent(JSON.stringify({ id: id, tempId: btn.verify.printTempId, pageId: btn.$MenuID || '', dataM: sessionStorage.getItem('dataM') }))))
      }, 200)
    }
  }
  sendWxMessage = (verify, id) => {
@@ -3169,7 +3205,7 @@
  }
  modelconfirm = () => {
    const { btn, BID } = this.props
    const { BID } = this.props
    const { btnconfig, selines } = this.state
    let _this = this
@@ -3314,7 +3350,7 @@
      this.execSubmit(selines, () => {}, result)
    } else {
      confirm({
        title: btn.tipTitle || '确定要执行吗?',
        title: btnconfig.setting.tipTitle || '确定要执行吗?',
        onOk() {
          return new Promise(resolve => {
            _this.execSubmit(selines, resolve, result)
@@ -3494,7 +3530,7 @@
  }
  render() {
    const { btn } = this.props
    const { btn, name } = this.props
    const { loadingNumber, loadingTotal, loading, disabled, hidden, check, count } = this.state
    if (hidden) return null
@@ -3513,42 +3549,25 @@
    }
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{name || btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || ''
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + (btn.class || 'unset')
      label = !loading ? <MkIcon type={btn.icon} /> : null
    } else if (btn.$toolbtn) {
      label = <span>{!loading && btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{loadingNumber && !loadingTotal ? `(${loadingNumber})` : ''}{btn.label}</span>
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + (btn.class || 'unset')
    }
    if (loadingNumber && btn.progress !== 'progressbar' && btn.$toolbtn && (!btn.show || btn.show === 'button')) {
      label = (loadingNumber ? `(${loadingNumber})` : '') + btn.label
      label = <span>{!loading && btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{name || btn.label}</span>
    }
    let BTN = <Button
      type={type}
      icon={icon}
      type="link"
      id={'button' + btn.uuid}
      title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
      loading={loading}
      disabled={disabled}
      style={btn.style}
      className={className}
      className={btn.$toolbtn ? (btn.hover || '') : ''}
      onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
    >{label}</Button>
src/tabviews/zshare/actionList/popupbutton/index.jsx
@@ -20,6 +20,7 @@
    btn: PropTypes.object,            // 按钮
    setting: PropTypes.any,           // 页面通用设置
    disabled: PropTypes.any,          // 行按钮禁用
    name: PropTypes.any
  }
  state = {
@@ -173,9 +174,9 @@
    let _data = null
    let primaryId = ''
    if (btn.Ot === 'requiredSgl' && setting.primaryKey) {
    if (btn.Ot === 'requiredSgl') {
      _data = data[0]
      primaryId = _data.$$uuid || _data[setting.primaryKey] || ''
      primaryId = _data.$$uuid || ''
    }
    this.setState({
@@ -303,46 +304,31 @@
  }
  render() {
    const { btn } = this.props
    const { btn, name } = this.props
    const { loading, disabled, hidden } = this.state
    if (hidden) return null
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{name || btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || ''
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = !loading ? <MkIcon type={btn.icon} /> : null
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <span>{!loading && btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{name || btn.label}</span>
    }
    return (
      <>
        <Button
          type={type}
          type="link"
          id={'button' + btn.uuid}
          title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
          loading={loading}
          loading={loading && !name}
          disabled={disabled}
          style={btn.style}
          icon={icon}
          className={className}
          className={btn.$toolbtn ? (btn.hover || '') : ''}
          onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
        >{label}</Button>
        <span onClick={(e) => {e.stopPropagation()}} onDoubleClick={(e) => {e.stopPropagation()}}>{this.getPop()}</span>
src/tabviews/zshare/actionList/printbutton/index.jsx
@@ -915,6 +915,9 @@
              _param.username = sessionStorage.getItem('User_Name') || ''
              _param.fullname = sessionStorage.getItem('Full_Name') || ''
            }
            if (btn.dataM === 'true') {
              _param.dataM = sessionStorage.getItem('dataM') === 'true' ? 'Y' : ''
            }
  
            return _param
          })
@@ -1007,7 +1010,11 @@
    }
    let isDataM = sessionStorage.getItem('dataM') === 'true'
    let custompage = /@pageSize@|@orderBy@/i.test(_dataresource + _customScript + _tailScript)
    let regoptions = [
      { reg: /@orderBy@/ig, value: btn.verify.setting.order },
      { reg: /@pageSize@/ig, value: '9999' },
      { reg: /@pageIndex@/ig, value: '1'},
      { reg: /@ID@/ig, value: `'${ID}'`},
      { reg: /@BID@/ig, value: `'${BID || ''}'`},
      { reg: /@LoginUID@/ig, value: `'${sessionStorage.getItem('LoginUID') || ''}'`},
@@ -1125,7 +1132,11 @@
    let LText = ''
    if (_dataresource) {
      LText = `/*system_query*/select ${arrFields} from (select ${arrFields} ,ROW_NUMBER() over(order by ${btn.verify.setting.order}) as rows from ${_dataresource}) tmptable order by tmptable.rows `
      if (custompage) {
        LText = `/*system_query*/select ${arrFields} from ${_dataresource} `
      } else {
        LText = `/*system_query*/select ${arrFields} from (select ${arrFields} ,ROW_NUMBER() over(order by ${btn.verify.setting.order}) as rows from ${_dataresource}) tmptable order by tmptable.rows `
      }
    }
    if (_customScript) {
@@ -2357,39 +2368,24 @@
    if (hidden) return null
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || ''
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = !loading ? <MkIcon type={btn.icon} /> : null
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <span>{!loading && btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{btn.label}</span>
    }
    return <>
      <Button
        type={type}
        type="link"
        id={'button' + btn.uuid}
        title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
        loading={loading}
        disabled={disabled}
        style={btn.style || null}
        icon={icon}
        className={className}
        className={btn.$toolbtn ? (btn.hover || '') : ''}
        onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
      >{label}</Button>
      <span onClick={(e) => {e.stopPropagation()}}>{this.getModels()}</span>
src/tabviews/zshare/actionList/shareLink/index.jsx
@@ -152,38 +152,22 @@
    const { loading } = this.state
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    let style = {...btn.style}
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || ''
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = !loading ? <MkIcon type={btn.icon} /> : null
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <span>{!loading && btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{btn.label}</span>
    }
    
    return (
      <Button
        type={type}
        type="link"
        title={btn.show === 'icon' ? btn.label : ''}
        style={style}
        loading={loading}
        icon={icon}
        className={className}
        style={btn.style || null}
        className={btn.$toolbtn ? (btn.hover || '') : ''}
        onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
      >{label}</Button>
    )
src/tabviews/zshare/actionList/tabbutton/index.jsx
@@ -1,11 +1,12 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Button, notification } from 'antd'
import { Button, notification, Dropdown } from 'antd'
import MKEmitter from '@/utils/events.js'
import MkIcon from '@/components/mk-icon'
// import './index.scss'
import './index.scss'
class TabButton extends Component {
  static propTpyes = {
@@ -15,6 +16,7 @@
    btn: PropTypes.object,            // 按钮
    selectedData: PropTypes.any,      // 子表中选择数据
    disabled: PropTypes.any,          // 行按钮禁用
    name: PropTypes.any
  }
  state = {
@@ -102,7 +104,7 @@
    const { btn, selectedData, BID } = this.props
    const { disabled } = this.state
    if (disabled) return
    if (disabled || btn.multiMenus) return
    if (triggerId && btn.uuid !== triggerId) return
    if (type === 'linkbtn' && !btn.$toolbtn && !is(fromJS(selectedData || []), fromJS(record))) {
@@ -137,8 +139,22 @@
      let ids = data.map(d => (d.$$uuid || ''))
      ids = ids.filter(Boolean)
      primaryId = ids.join(',')
    } else if (btn.Ot === 'notRequired' && BID) {
      primaryId = BID
    } else if (btn.Ot === 'notRequired') {
      if (btn.sysId === 'js') {
        primaryId = (() => {
          let uuid = []
          let timestamp = new Date().getTime()
          let _options = '0123456789abcdefghigklmnopqrstuv'
          for (let i = 0; i < 19; i++) {
            uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1))
          }
          uuid = timestamp + uuid.join('')
          return uuid
        })()
        primaryId = primaryId + (btn.sign || '')
      } else {
        primaryId = BID || ''
      }
    }
    let newtab = {}
@@ -196,45 +212,132 @@
    }
  }
  triggerMenu = (tab) => {
    const { btn, selectedData, BID } = this.props
    let data = selectedData || []
    if (btn.Ot !== 'notRequired' && data.length === 0) {
      // 需要选择行时,校验数据
      notification.warning({
        top: 92,
        message: '请选择行!',
        duration: 5
      })
      return false
    } else if (btn.Ot === 'requiredSgl' && data.length !== 1) {
      // 需要选择单行时,校验数据
      notification.warning({
        top: 92,
        message: '请选择单行数据!',
        duration: 5
      })
      return
    }
    let primaryId = ''
    if (btn.Ot === 'requiredSgl') {
      primaryId = data[0].$$uuid || ''
    } else if (btn.Ot === 'requiredOnce') {
      let ids = data.map(d => (d.$$uuid || ''))
      ids = ids.filter(Boolean)
      primaryId = ids.join(',')
    } else if (btn.Ot === 'notRequired') {
      if (btn.sysId === 'js') {
        primaryId = (() => {
          let uuid = []
          let timestamp = new Date().getTime()
          let _options = '0123456789abcdefghigklmnopqrstuv'
          for (let i = 0; i < 19; i++) {
            uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1))
          }
          uuid = timestamp + uuid.join('')
          return uuid
        })()
        primaryId = primaryId + (tab.sign || '')
      } else {
        primaryId = BID || ''
      }
    }
    let menuId = tab.menuId.slice(-1)[0]
    let menu = null
    if (window.GLOB.mkThdMenus.has(menuId)) {
      menu = {...window.GLOB.mkThdMenus.get(menuId), param: { $BID: primaryId }}
    } else if (tab.MenuID) {
      menu = {
        MenuID: tab.MenuID,
        MenuName: tab.MenuName,
        type: tab.tabType,
        param: { $BID: primaryId }
      }
    }
    if (!menu) {
      notification.warning({
        top: 92,
        message: '菜单已删除或没有访问权限!',
        duration: 5
      })
      return
    }
    if (btn.openTab === 'view') {
      window.open('#/view/' + menu.MenuID + '/' + primaryId)
    } else {
      MKEmitter.emit('modifyTabs', menu, true)
      MKEmitter.emit('openNewTab')
    }
    if (window.GLOB.systemType === 'production') {
      MKEmitter.emit('queryTrigger', {menuId: btn.uuid, name: '标签页'})
    }
  }
  render() {
    const { btn } = this.props
    const { btn, name } = this.props
    const { disabled, hidden } = this.state
    if (hidden) return null
    let label = ''
    let icon = ''
    let type = 'link'
    let className = ''
    if (btn.show === 'button') {
      label = btn.label
      icon = btn.icon || ''
    } else if (btn.show === 'link') {
      label = <span>{btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
      icon = ''
    if (btn.show === 'link') {
      label = <span>{name || btn.label}{btn.icon ? <MkIcon style={{marginLeft: '8px'}} type={btn.icon} /> : ''}</span>
    } else if (btn.show === 'icon') {
      icon = btn.icon || ''
    } else if (!btn.$toolbtn) {
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <MkIcon type={btn.icon} />
    } else {
      type = ''
      icon = btn.icon || ''
      label = btn.label
      className = 'mk-btn mk-' + btn.class
      label = <span>{btn.icon ? <MkIcon style={{marginRight: '8px'}} type={btn.icon} /> : ''}{name || btn.label}</span>
    }
    if (btn.multiMenus && !disabled) {
      return (<Dropdown
        overlay={<div className="mk-tab-dropdown-wrap">{
          btn.multiMenus.map(tab => <div key={tab.uuid} onClick={() => this.triggerMenu(tab)}>{tab.name}</div>)
        }</div>}
        trigger={['hover']}
      >
        <Button
          type="link"
          title={btn.show === 'icon' ? btn.label : ''}
          style={btn.style || null}
          className={btn.$toolbtn ? (btn.hover || '') : ''}
        >{label}</Button>
      </Dropdown>)
    }
    return (
      <Button
        type={type}
        type="link"
        id={'button' + btn.uuid}
        title={disabled ? (btn.reason || '') : (btn.show === 'icon' ? btn.label : '')}
        style={btn.style || null}
        disabled={disabled}
        icon={icon}
        className={className}
        className={btn.$toolbtn ? (btn.hover || '') : ''}
        onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
      >{label}</Button>
    )
src/tabviews/zshare/actionList/tabbutton/index.scss
@@ -1 +1,19 @@
.mk-tab-dropdown-wrap {
  box-shadow: 0 0 2px #bcbcbc;
  background: #ffffff;
  min-width: 85px;
  border-radius: 2px;
  div {
    height: 32px;
    line-height: 32px;
    padding: 0 15px;
    color: rgba(0, 0, 0, 0.65);
    background: #ffffff;
    cursor: pointer;
  }
  div:hover {
    background-color: var(--mk-sys-color);
    color: #ffffff;
  }
}
src/tabviews/zshare/automatic/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Button, message } from 'antd'
import { Button, message, Tooltip } from 'antd'
import MKEmitter from '@/utils/events.js'
// import './index.scss'
@@ -224,7 +224,9 @@
    return (
      <div className="tool-wrap">
        <Button icon={running ? 'pause' : 'forward'} shape="circle" onClick={this.trigger}/>
        <Tooltip placement="left" title="无人值守">
          <Button icon={running ? 'pause' : 'forward'} shape="circle" onClick={this.trigger}/>
        </Tooltip>
      </div>
    )
  }
src/tabviews/zshare/flowFloat/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Button, Modal, notification, Spin, Empty } from 'antd'
import { Button, Modal, notification, Spin, Empty, Tooltip } from 'antd'
import { BranchesOutlined } from '@ant-design/icons'
import Api from '@/api'
@@ -191,7 +191,9 @@
    return (
      <div className="tool-wrap">
        <Button shape="circle" onClick={this.trigger}><BranchesOutlined /></Button>
        <Tooltip placement="left" title="工作流">
          <Button shape="circle" onClick={this.trigger}><BranchesOutlined /></Button>
        </Tooltip>
        <Modal
          wrapClassName="flow-setting-modal"
          title="工作流"
src/tabviews/zshare/mutilform/index.jsx
@@ -148,6 +148,7 @@
      item.readin = item.readin !== 'false' && item.readin !== 'top'
      item.readonly = check || item.readonly === 'true'
      item.writein = item.writein !== 'false'
      item.defHidden = item.hidden === 'true'
      item.hidden = item.hidden === 'true'
      item.fieldlength = item.fieldlength || 50
@@ -497,7 +498,7 @@
        let _hidden = false
        if (supItem.hidden) {
        if (supItem.hidden && !supItem.defHidden) {
          _hidden = true
        } else {
          let box = [...item.values]
@@ -1280,7 +1281,7 @@
        } else if (item.type === 'radio') {
          content = (<MKRadio config={item} onChange={(val, other) => this.recordChange({[item.field]: val, ...other}, item)}/>)
        } else if (item.type === 'date' || item.type === 'datemonth') {
          content = (<MKDatePicker config={item} onChange={(val) => this.recordChange({[item.field]: val})} />)
          content = (<MKDatePicker config={item} onChange={(val) => this.recordChange({[item.field]: val})} onSubmit={this.props.inputSubmit}/>)
        } else if (item.type === 'fileupload') {
          className = item.readonly ? 'readonly' : ''
          className += item.fileType === 'picture-card' ? ' file-upload' : ''
src/tabviews/zshare/mutilform/mkDatePicker/index.jsx
@@ -3,6 +3,8 @@
import { DatePicker } from 'antd'
import moment from 'moment'
import MKEmitter from '@/utils/events.js'
const { MonthPicker } = DatePicker
/**
@@ -68,6 +70,17 @@
    }
    this.props.onChange(_val)
    if (!config.enter || config.enter === 'false') return
    setTimeout(() => {
      if (config.enter === 'tab') {
        MKEmitter.emit('mkFC', 'focus', config.tabUuid)
      } else if (config.enter === 'sub') {
        config.tabUuid && MKEmitter.emit('mkFC', 'focus', config.tabUuid)
        this.props.onSubmit(config.tabUuid)
      }
    }, 50)
  }
  disabledDate = (current) => {
src/tabviews/zshare/mutilform/mkNumberInput/index.jsx
@@ -78,8 +78,14 @@
  render() {
    const { config } = this.props
    const { value, precision } = this.state
    if (precision === null) {
    if (config.format === 'thdSeparator') {
      if (precision === null) {
        return (<InputNumber id={config.uuid} formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')} parser={value => value.replace(/,*/g, '')} value={value} disabled={config.readonly} onChange={this.handleChange} onPressEnter={this.handleSubmit}/>)
      } else {
        return (<InputNumber id={config.uuid} formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')} parser={value => value.replace(/,*/g, '')} value={value} precision={precision} disabled={config.readonly} onChange={this.handleChange} onPressEnter={this.handleSubmit} />)
      }
    } else if (precision === null) {
      return (<InputNumber id={config.uuid} value={value} disabled={config.readonly} onChange={this.handleChange} onPressEnter={this.handleSubmit}/>)
    } else {
      return (<InputNumber id={config.uuid} value={value} precision={precision} disabled={config.readonly} onChange={this.handleChange} onPressEnter={this.handleSubmit} />)
src/tabviews/zshare/normalTable/index.jsx
@@ -344,11 +344,14 @@
    e.stopPropagation()
    let __param = {
      $searchkey: item.field,
      $searchval: record[item.field] || '',
      $BID: record.$$uuid
    }
    if (item.field) {
      __param.$searchkey = item.field.toLowerCase()
      __param.$searchval = record[item.field] || ''
    }
    if (item.linkfields && item.linkfields.length > 0) {
      item.linkfields.forEach(field => {
        __param[field] = record[field] || ''
src/tabviews/zshare/normalTable/index.scss
@@ -10,11 +10,8 @@
    position: absolute;
    bottom: 40px;
  }
  >.ant-table-wrapper {
    position: relative;
    z-index: 1;
  }
  .ant-table-wrapper {
    position: relative;
    color: rgba(0, 0, 0, 0.65);
    font-size: 14px;
    .ant-table {
src/tabviews/zshare/settingcomponent/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Button, Modal, notification, Spin, Empty } from 'antd'
import { Button, Modal, notification, Spin, Empty, Tooltip } from 'antd'
import Api from '@/api'
import MKEmitter from '@/utils/events.js'
@@ -522,10 +522,12 @@
    return (
      <div className="tool-wrap">
        <Button icon="setting" shape="circle" onClick={this.trigger}/>
        <Tooltip placement="left" title="自定义设置">
          <Button icon="setting" shape="circle" onClick={this.trigger}/>
        </Tooltip>
        <Modal
          wrapClassName="custom-setting-modal"
          title={'自定义设置'}
          title="自定义设置"
          maskClosable={false}
          width={950}
          visible={visible}
src/tabviews/zshare/tablenodes/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal, Button, notification, Spin, Input, Typography, message } from 'antd'
import { Modal, Button, notification, Spin, Input, Typography, message, Tooltip } from 'antd'
import Api from '@/api'
import G6 from "@antv/g6"
@@ -477,7 +477,9 @@
    return (
      <div className="tool-wrap">
        <Button icon="fork" shape="circle" onClick={this.trigger}/>
        <Tooltip placement="left" title="表关系图">
          <Button icon="fork" shape="circle" onClick={this.trigger}/>
        </Tooltip>
        <Modal
          title=""
          wrapClassName="view-table-modal"
src/tabviews/zshare/topSearch/advanceform/index.scss
@@ -7,6 +7,10 @@
    float: none;
    vertical-align: top;
  }
  .ant-checkbox-wrapper {
    color: rgba(0, 0, 0, 0.85);
    white-space: nowrap;
  }
  .ant-form-item {
    display: flex;
    margin-bottom: 0px;
@@ -32,6 +36,9 @@
  .ant-select-dropdown {
    z-index: 10 !important;
  }
  .ant-radio-group {
    white-space: nowrap;
  }
  .ant-calendar-picker-container {
    z-index: 10 !important;
  }
src/tabviews/zshare/topSearch/index.jsx
@@ -42,7 +42,7 @@
  sign = ''
  UNSAFE_componentWillMount () {
    const { config, BID } = this.props
    const { config } = this.props
    let _searchlist = []
    let fieldMap = new Map()
@@ -54,6 +54,7 @@
    let record = {}
    let forbid = false // header中不设置高级搜索
    let _setting = {showAdv: false, show: false, style: null}
    let BID = this.props.BID
    
    if (config.wrap) {
      _setting.show = config.wrap.show !== 'false'
@@ -66,6 +67,15 @@
      _setting.wrapperCol = {style: {width: (100 - _setting.labelwidth) + '%'}}
      _setting.borderRadius = config.wrap.borderRadius
      _setting.resetContrl = config.wrap.resetContrl || 'init'
      if (config.wrap.searchBtn === 'show') {
        _setting.showBtn = true
      }
      if (config.wrap.supModule) {
        let BData = window.GLOB.CacheData.get(config.wrap.supModule)
        BID = BData ? (BData.$BID || '') : ''
      }
    }
    
    if (config.type === 'search') {
@@ -278,19 +288,42 @@
      searchlist: _list
    }, () => {
      if (!window.GLOB.mkHS && window.GLOB.sysType === 'local' && window.GLOB.systemType !== 'production') {
        this.improveSimpleSearch(deForms, false)
        this.improveSimpleSearch(deForms, false, null, BID)
      } else if (mainItems.length > 0 || localItems.length > 0) {
        this.improveSearch(mainItems, localItems)
        this.improveSearch(mainItems, localItems, BID)
      }
    })
  }
  componentDidMount () {
    const { config } = this.props
    if (config.type === 'search' && config.wrap.supModule) {
      MKEmitter.addListener('resetSelectLine', this.resetParentParam)
    }
  }
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    const { config, BID } = this.props
    if (config.checkBid && nextProps.BID !== BID) {
    if (config.checkBid && config.type !== 'search' && nextProps.BID !== BID) {
      this.resetOptions(nextProps.BID)
    }
  }
  resetParentParam = (MenuID, id) => {
    const { config } = this.props
    if (config.wrap.supModule !== MenuID) return
    this.resetOptions(id)
  }
  resetOptions = (BID) => {
@@ -346,7 +379,7 @@
    })
    if (deForms.length > 0) {
      this.improveSimpleSearch(deForms, true, searchlist)
      this.improveSimpleSearch(deForms, true, searchlist, BID)
    } else {
      this.setState({
        searchlist: searchlist
@@ -391,8 +424,7 @@
  }
  // 查询下拉菜单
  improveSearch = (mainItems, localItems) => {
    const { BID } = this.props
  improveSearch = (mainItems, localItems, BID) => {
    let deffers = []
    // 本地请求
@@ -495,7 +527,7 @@
  }
  // 测试系统单个请求下拉选项
  improveSimpleSearch = (deForms, trigger, searchlist) => {
  improveSimpleSearch = (deForms, trigger, searchlist, BID) => {
    if (deForms.length === 0) return
    let deffers = deForms.map((item, index) => {
@@ -506,8 +538,8 @@
        arr_field: item.arr_field
      }
      if (this.props.BID) {
        param.BID = this.props.BID
      if (BID) {
        param.BID = BID
      }
      if (window.GLOB.execType === 'x') {
@@ -569,11 +601,11 @@
          }
          if (item.linkField) {
            _item.ParentID = cell[item.linkField]
            _item.ParentID = cell[item.linkField] + ''
          }
          if (item.type !== 'checkcard') {
            _item.Value = cell[item.valueField]
            _item.Value = cell[item.valueField] + ''
            _item.Text = cell[item.valueText] + ''
            
            if (map.has(_item.ParentID + _item.Value)) return
@@ -588,7 +620,7 @@
            
            map.set(_item.ParentID + _item.Value, 0)
          } else {
            _item.$value = cell[item.cardValField]
            _item.$value = cell[item.cardValField] + ''
            _item = {..._item, ...cell}
            if (item.urlField) {
@@ -761,6 +793,16 @@
          </Form.Item>
        </Col>
      )
    } else if (setting.showBtn) {
      fields.push(
        <Col className="mk-search-col search-button" key="actions">
          <Form.Item>
            <Button type="primary" onClick={this.handleSubmit}>
              搜索
            </Button>
          </Form.Item>
        </Col>
      )
    }
    
    return fields
src/tabviews/zshare/topSearch/index.scss
@@ -5,9 +5,11 @@
    display: inline-block;
    float: none;
    vertical-align: top;
    text-align: left;
  }
  .ant-checkbox-wrapper {
    color: rgba(0, 0, 0, 0.85);
    white-space: nowrap;
  }
  .ant-form-item {
    display: flex;
@@ -34,6 +36,9 @@
  .ant-select-dropdown {
    z-index: 10 !important;
  }
  .ant-radio-group {
    white-space: nowrap;
  }
  .ant-calendar-picker-container {
    z-index: 10 !important;
  }
@@ -50,7 +55,7 @@
  }
  .search-button {
    min-height: 55px;
    .ant-btn-link, .ant-btn-link:hover, .ant-btn-link:active{
    .ant-btn-link, .ant-btn-link:hover, .ant-btn-link:active {
      border-color: transparent;
      span {
        position: relative;
src/tabviews/zshare/topSearch/mkDatePicker/index.jsx
@@ -54,10 +54,21 @@
    const { config } = this.props
    if (config.checkShift && nextProps.config.initval && nextProps.config.initval !== config.initval) {
      let val = nextProps.config.initval.split(',')
      let value = nextProps.config.initval || null
      if (this.state.mode === 'daterange') {
        if (value) {
          let val = value.split(',')
          value = [moment(val[0], this.state.format), moment(val[1], this.state.format)]
        } else {
          value = [null, null]
        }
      } else if (value) {
        value = moment(value, this.state.format)
      }
      this.setState({
        value: [moment(val[0], config.format), moment(val[1], config.format)]
        value: value
      })
      this.props.onChange(nextProps.config.initval, true)
src/templates/modalconfig/index.jsx
@@ -264,7 +264,7 @@
        })
      }
      if (item.type === 'switch' || item.type === 'check') {
      if (['switch', 'check', 'popSelect'].includes(item.type)) {
        _linksupFields.push({
          field: item.field,
          label: item.label
src/templates/modalconfig/settingform/index.jsx
@@ -347,8 +347,13 @@
              )}
            </Form.Item>
          </Col> : null}
          {display === 'drawer' && appType !== 'mob' ? <Col span={12}>
            <Form.Item label="表单类型">
          {appType !== 'mob' && (display === 'drawer' || display === 'modal') ? <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="选择查看时表单均为只读,仅用于数据展示。">
                <QuestionCircleOutlined className="mk-form-tip" />
                表单类型
              </Tooltip>
            }>
              {getFieldDecorator('formType', {
                initialValue: config.setting.formType || 'edit'
              })(
@@ -359,6 +364,20 @@
              )}
            </Form.Item>
          </Col> : null}
          {display === 'prompt' ? <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="提示框的确认提示信息。">
                <QuestionCircleOutlined className="mk-form-tip" />
                确认提示
              </Tooltip>
            }>
              {getFieldDecorator('tipTitle', {
                initialValue: config.setting.tipTitle || ''
              })(
                <Input autoComplete="off" onPressEnter={this.handleSubmit} />
              )}
            </Form.Item>
          </Col> : null}
          {!this.props.isSubTab && !appType && this.state.viewType !== 'popview' && display === 'modal' ? <Col span={12}>
            <Form.Item label="挂载对象">
              {getFieldDecorator('container', {
src/templates/sharecomponent/actioncomponent/actionform/index.jsx
@@ -13,7 +13,7 @@
const { TextArea } = Input
const actionTypeOptions = {
  pop: ['label', 'position', 'OpenType', 'intertype', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'openmenu', 'output', 'tipTitle', 'hidden'],
  pop: ['label', 'position', 'OpenType', 'intertype', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'openmenu', 'output', 'hidden'],
  prompt: ['label', 'position', 'OpenType', 'intertype', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'openmenu', 'output', 'tipTitle', 'hidden'],
  exec: ['label', 'position', 'OpenType', 'intertype', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'openmenu', 'output', 'hidden'],
  excelIn: ['label', 'Ot', 'OpenType', 'intertype', 'icon', 'class', 'sheet', 'execSuccess', 'execError', 'hidden'],
src/templates/sharecomponent/actioncomponent/formconfig.jsx
@@ -523,7 +523,7 @@
      key: 'tipTitle',
      label: '确认提示',
      initVal: card.tipTitle || '',
      tooltip: '注:弹窗(表单)在显示为是否框时有效。',
      tooltip: '提示框的确认提示信息。',
      required: false
    },
    {
src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx
@@ -283,7 +283,7 @@
          </Col> : null}
          {!type ? <Col span={10}>
            <Form.Item label="报错字段" style={{margin: 0, whiteSpace: 'nowrap'}}>
              ErrorCode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
              errorcode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
            </Form.Item>
          </Col> : null}
          {!type ? <Col span={24} className="sqlfield">
src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
@@ -716,39 +716,46 @@
        if (!err) {
          let _verify = {...verify, ...values}
          if (_verify.excelHandle !== 'true') {
          if (_verify.excelHandle !== 'true') {
            delete _verify.excel_func
          }
          if (_verify.default === 'false' && _verify.scripts.length === 0) {
            notification.warning({
              top: 92,
              message: '不执行默认sql时,必须设置自定义脚本!',
              duration: 5
            })
            return
          }
          let cols = _verify.columns.map(col => col.Column.toLowerCase())
          cols = Array.from(new Set(cols))
          let error = ''
          if (_verify.columns.length === 0) {
            notification.warning({
              top: 92,
              message: '请设置Excel列字段!',
              duration: 5
            })
            return
            error = '请设置Excel列字段!'
          } else if (_verify.columns.length > cols.length) {
            notification.warning({
              top: 92,
              message: 'Excel列字段名,不可重复!',
              duration: 5
            })
            return
            error = 'Excel列字段名,不可重复!'
          } else if (cols.includes('bid')) {
            error = 'bid字段为保留字,不可使用!'
          } else if (cols.includes('jskey')) {
            error = 'jskey字段为保留字,不可使用!'
          } else if (_verify.range === 1) {
            let tEmptys = _verify.columns.filter(op => !op.Text)
            if (tEmptys.length > 0) {
              notification.warning({
                top: 92,
                message: '忽略首行时,会使用Text值校验Excel首行内容,Text值与Excel表首行内容相同,且均不可为空!',
                duration: 5
              })
              return
              error = '忽略首行时,会使用Text值校验Excel首行内容,Text值与Excel表首行内容相同,且均不可为空!'
            }
          }
          if (error) {
            notification.warning({
              top: 92,
              message: error,
              duration: 5
            })
            return
          }
          _verify.columns.sort((a, b) => {
            if (a.import === 'init' && b.import !== 'init') {
              return 1
src/templates/sharecomponent/actioncomponent/verifyexcelin/index.scss
@@ -83,10 +83,6 @@
  .custom-table .ant-empty {
    margin: 20px 8px!important;
  }
  .excel-custom-table {
    position: relative;
    top: -25px;
  }
  .errorval {
    display: inline-block;
    width: 30px;
src/templates/sharecomponent/actioncomponent/verifyexcelout/customscript/index.jsx
@@ -9,6 +9,7 @@
import Utils from '@/utils/utils.js'
import { checkSQL, getSearchFields } from '@/utils/utils-custom.js'
import CodeMirror from '@/templates/zshare/codemirror'
import MKEmitter from '@/utils/events.js'
// import './index.scss'
class CustomForm extends Component {
@@ -134,6 +135,9 @@
              loading: false,
              editItem: null
            })
            if (values.uuid) {
              MKEmitter.emit('editLineId', values.uuid)
            }
            this.props.form.setFieldsValue({
              sql: ' '
            })
@@ -206,14 +210,14 @@
          </Col>
          <Col span={10}>
            <Form.Item label="报错字段" style={{margin: 0, whiteSpace: 'nowrap'}}>
              ErrorCode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
              errorcode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
            </Form.Item>
          </Col>
          <Col span={24} className="sqlfield">
            <Form.Item label="可用字段">
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'公共值,请按照@xxx@格式使用。'}><span style={{color: '#1890ff'}}>BID, ID, LoginUID, SessionUid, UserID, Appkey, time_id, typename</span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'系统变量,系统会定义变量并赋值。'}><span style={{color: '#fa8c16'}}>UserName, FullName, RoleID, mk_departmentcode, mk_organization, mk_user_type, mk_nation, mk_province, mk_city, mk_district, mk_address</span></Tooltip>
              {usefulfields ? <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'搜索条件,请按照@xxx@格式使用。'}>,&nbsp;{usefulfields}</Tooltip> : null}
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'排序、分页以及搜索条件变量,请按照@xxx@格式使用。'}>, orderBy, pageSize, pageIndex{usefulfields ? ', ' + usefulfields : ''}</Tooltip>
              {linefields ? <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'表单及行变量,系统会定义变量并赋值。'}>,&nbsp;{linefields}</Tooltip> : null}
            </Form.Item>
          </Col>
src/templates/sharecomponent/actioncomponent/verifyexcelout/datasource/index.jsx
@@ -248,6 +248,18 @@
            </Col> : null}
            {btnType !== 'print' && excelHandle !== 'true' ? <Col span={8}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="导出excel的文件名,默认为菜单名+组件名。">
                  <QuestionCircleOutlined className="mk-form-tip" />
                  文件名
                </Tooltip>
              }>
                {getFieldDecorator('excelName', {
                  initialValue: setting.excelName || ''
                })(<Input autoComplete="off" />)}
              </Form.Item>
            </Col> : null}
            {btnType !== 'print' && excelHandle !== 'true' ? <Col span={8}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="导出excel中工作表名称,默认为Sheet1。">
                  <QuestionCircleOutlined className="mk-form-tip" />
                  工作表
@@ -255,7 +267,7 @@
              }>
                {getFieldDecorator('sheet', {
                  initialValue: setting.sheet || ''
                })(<Input placeholder="" autoComplete="off" />)}
                })(<Input autoComplete="off" />)}
              </Form.Item>
            </Col> : null}
            {btnType !== 'print' && excelHandle !== 'true' ? <Col span={8}>
@@ -286,7 +298,7 @@
                </Radio.Group>)}
              </Form.Item>
            </Col> : null}
            {btnType !== 'print' && excelHandle !== 'true' ? <Col span={8}>
            {/* {btnType !== 'print' && excelHandle !== 'true' ? <Col span={8}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="Excel内容区是否自动换行。">
                  <QuestionCircleOutlined className="mk-form-tip" />
@@ -301,7 +313,7 @@
                  <Radio value="true">是</Radio>
                </Radio.Group>)}
              </Form.Item>
            </Col> : null}
            </Col> : null} */}
          </Row>
        </Form>
      </div>
src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx
@@ -9,6 +9,7 @@
import Utils from '@/utils/utils.js'
import SettingUtils from './utils.jsx'
import ColumnForm from './columnform'
import OtherForm from './otherform'
import DataSource from './datasource'
import CustomScript from './customscript'
import asyncComponent from '@/utils/asyncComponent'
@@ -30,6 +31,8 @@
    verify: {},
    debugId: '',
    activeKey: 'setting',
    visible: false,
    line: null,
    searchKey: '',
    excelColumns: [
      {
@@ -68,7 +71,7 @@
        editable: true,
        required: false,
        keyCol: true,
        width: '8%',
        width: '10%',
        render: (text) => {
          if (text === 'image') {
            return '图片'
@@ -115,50 +118,50 @@
          {value: 'false', text: '否'}
        ]
      },
      {
        title: '取绝对值',
        dataIndex: 'abs',
        inputType: 'radio',
        editable: true,
        required: false,
        keyVals: ['number'],
        width: '10%',
        render: (text, record) => {
          if (record.type !== 'number') return ''
      // {
      //   title: '取绝对值',
      //   dataIndex: 'abs',
      //   inputType: 'radio',
      //   editable: true,
      //   required: false,
      //   keyVals: ['number'],
      //   width: '10%',
      //   render: (text, record) => {
      //     if (record.type !== 'number') return ''
          if (text === 'true') {
            return '是'
          } else {
            return '否'
          }
        },
        options: [
          {value: 'true', text: '是'},
          {value: 'false', text: '否'}
        ]
      },
      {
        title: '0值导出',
        dataIndex: 'noValue',
        inputType: 'radio',
        editable: true,
        required: false,
        keyVals: ['number'],
        width: '10%',
        render: (text, record) => {
          if (record.type !== 'number') return ''
      //     if (text === 'true') {
      //       return '是'
      //     } else {
      //       return '否'
      //     }
      //   },
      //   options: [
      //     {value: 'true', text: '是'},
      //     {value: 'false', text: '否'}
      //   ]
      // },
      // {
      //   title: '0值导出',
      //   dataIndex: 'noValue',
      //   inputType: 'radio',
      //   editable: true,
      //   required: false,
      //   keyVals: ['number'],
      //   width: '10%',
      //   render: (text, record) => {
      //     if (record.type !== 'number') return ''
          if (text !== 'false') {
            return '是'
          } else {
            return '否'
          }
        },
        options: [
          {value: 'true', text: '是'},
          {value: 'false', text: '否'}
        ]
      },
      //     if (text !== 'false') {
      //       return '是'
      //     } else {
      //       return '否'
      //     }
      //   },
      //   options: [
      //     {value: 'true', text: '是'},
      //     {value: 'false', text: '否'}
      //   ]
      // },
      {
        title: '小数位',
        dataIndex: 'decimal',
@@ -170,7 +173,68 @@
        keyVals: ['number'],
        width: '12%',
        render: (text, record) => record.type === 'number' ? text : ''
      }
      },
      {
        title: '其他',
        dataIndex: 'other',
        required: false,
        width: '18%',
        render: (_, record) => {
          if (record.type === 'number') {
            let val = ''
            if (record.abs === 'true') {
              val += '取绝对值;'
            }
            if (record.noValue === 'false') {
              val += '0值不导出;'
            }
            if (record.format) {
              if (record.format === 'thdSeparator') {
                val += '格式化:千分位;'
              } else if (record.format === 'thdSepPm') {
                val += '格式化:千分位(负值红色);'
              } else if (record.format === 'percent') {
                val += '格式化:百分比;'
              }
            }
            if (record.prefix) {
              val += `前缀:${record.prefix};`
            }
            if (record.postfix) {
              val += `后缀:${record.postfix};`
            }
            return <div>{val}<EditOutlined className="edit-other" onClick={() => {this.setState({visible: true, line: fromJS(record).toJS()})}} /></div>
          } else if (record.type === 'text') {
            let val = ''
            if (record.wrapText === 'true') {
              val += '自动换行;'
            }
            if (record.noValue === 'false') {
              val += '空值不导出;'
            }
            if (record.textFormat) {
              if (record.textFormat === 'YYYY-MM-DD') {
                val += '格式化:YYYY-MM-DD;'
              } else if (record.textFormat === 'YYYY-MM-DD HH:mm:ss') {
                val += '格式化:YYYY-MM-DD HH:mm:ss;'
              }
            }
            if (record.prefix) {
              val += `前缀:${record.prefix};`
            }
            if (record.postfix) {
              val += `后缀:${record.postfix};`
            }
            return <div>{val}<EditOutlined className="edit-other" onClick={() => {this.setState({visible: true, line: fromJS(record).toJS()})}} /></div>
          }
          return ''
        }
      },
    ],
    scriptsColumns: [
      {
@@ -227,12 +291,12 @@
        dataIndex: 'operation',
        render: (text, record) =>
          (<div style={{textAlign: 'center'}}>
            <span className="operation-btn" title="编辑" onClick={() => this.handleEdit(record, 'scripts')} style={{color: '#1890ff'}}><EditOutlined /></span>
            <span className="operation-btn" title="状态切换" onClick={() => this.handleStatus(record, 'scripts')} style={{color: '#8E44AD'}}><SwapOutlined /></span>
            <span className="operation-btn" title="编辑" onClick={() => this.handleEdit(record)} style={{color: '#1890ff'}}><EditOutlined /></span>
            <span className="operation-btn" title="状态切换" onClick={() => this.handleStatus(record)} style={{color: '#8E44AD'}}><SwapOutlined /></span>
            <Popconfirm
              overlayClassName="popover-confirm"
              title="确定删除吗?"
              onConfirm={() => this.handleDelete(record, 'scripts')
              onConfirm={() => this.handleDelete(record)
            }>
              <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
            </Popconfirm>
@@ -392,14 +456,9 @@
    })
  }
  handleEdit = (record, type) => {
    let node = null
    if (type === 'scripts') {
      this.scriptsForm.edit(record)
      node = document.getElementById('mk-exout-script')
    }
  handleEdit = (record) => {
    this.scriptsForm.edit(record)
    let node = document.getElementById('mk-exout-script')
    if (node && node.scrollTop) {
      let inter = Math.ceil(node.scrollTop / 10)
@@ -415,19 +474,17 @@
    }
  }
  handleStatus = (record, type) => {
  handleStatus = (record) => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    record.status = record.status === 'false' ? 'true' : 'false'
    if (type === 'scripts') {
      verify.scripts = verify.scripts.map(item => {
        if (item.uuid === record.uuid) {
          return record
        } else {
          return item
        }
      })
    }
    verify.scripts = verify.scripts.map(item => {
      if (item.uuid === record.uuid) {
        return record
      } else {
        return item
      }
    })
    this.setState({
      verify: verify
@@ -459,38 +516,12 @@
    })
  }
  handleDelete = (record, type) => {
    const { verify } = this.state
  handleDelete = (record) => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    verify.columns = verify.columns.filter(item => item.uuid !== record.uuid)
    verify.scripts = verify.scripts.filter(item => item.uuid !== record.uuid)
    this.setState({ verify: verify })
  }
  handleUpDown = (record, type, direction) => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    let index = 0
    verify.columns = verify.columns.filter((item, i) => {
      if (item.uuid === record.uuid) {
        index = i
      }
      return item.uuid !== record.uuid
    })
    if ((index === 0 && direction === 'up') || (index === verify.columns.length && direction === 'down')) {
      return
    }
    if (direction === 'up') {
      verify.columns.splice(index - 1, 0, record)
    } else {
      verify.columns.splice(index + 1, 0, record)
    }
    this.setState({
      verify: verify
    })
  }
  showError = (errorType) => {
@@ -626,7 +657,7 @@
                loading: false
              })
              reject()
            }, verify.scripts)
            }, verify.scripts, 'submit')
          })
        })
      } else if (activeKey === 'columns') {
@@ -645,7 +676,7 @@
              loading: false
            })
            reject()
          }, verify.scripts)
          }, verify.scripts, 'submit')
        }
      } else if (activeKey === 'scripts') {
        this.setState({loading: true})
@@ -659,7 +690,7 @@
            loading: false
          })
          reject()
        }, verify.scripts)
        }, verify.scripts, 'submit')
      } else {
        resolve(verify)
      }
@@ -812,27 +843,30 @@
          col.type = 'text'
        }
      }
      if (col.type === 'text' && col.format) {
        delete col.format
      } else if (col.type === 'number' && col.textFormat) {
        delete col.textFormat
      }
      return col
    })
    // if (columns[0] && !['image', 'text', 'number'].includes(columns[0].type)) {
    //   columns = columns.map(col => {
    //     let _cell = {
    //       uuid: Utils.getuuid(),
    //       Column: col.Column,
    //       Text: col.Text,
    //       Width: 20,
    //       abs: 'false',
    //       output: col.output || 'true',
    //       required: col.required || 'false',
    //       type: 'text',
    //     }
    //     return _cell
    //   })
    // }
    this.setState({verify: {...verify, columns}})
  }
  lineSubmit = () => {
    const { line, verify } = this.state
    let columns = verify.columns.map(col => {
      if (col.uuid === line.uuid) {
        return line
      }
      return col
    })
    this.setState({verify: {...verify, columns}, line: null, visible: false})
  }
  // 标签切换
@@ -956,7 +990,7 @@
    }
  }
  sqlverify = (_resolve, _reject, scripts) => {
  sqlverify = (_resolve, _reject, scripts, type) => {
    const { searches, verify, debugId } = this.state
    if (verify.dataType !== 'custom') {
@@ -978,10 +1012,24 @@
        this.setState({debugId: _debugId})
        _resolve()
      } else {
        _reject()
        Modal.error({
          title: result.message
        })
        if (type === 'submit') {
          Modal.confirm({
            title: result.message,
            okText: '知道了',
            cancelText: '强制保存',
            onOk: () => {
              _reject()
            },
            onCancel() {
              _resolve()
            }
          })
        } else {
          _reject()
          Modal.error({
            title: result.message
          })
        }
      }
    })
  }
@@ -1007,7 +1055,7 @@
  render() {
    const { card } = this.props
    const { verify, excelColumns, scriptsColumns, activeKey, loading, searches, searchKey } = this.state
    const { verify, excelColumns, scriptsColumns, activeKey, loading, searches, searchKey, visible, line } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -1040,7 +1088,7 @@
            <Button className="excel-col-add mk-red" title="清空Excel列" onClick={this.clearField}>
              清空Excel列
            </Button>
            <div style={{color: '#959595', fontSize: '13px', paddingLeft: '10px'}}>如需导出序号,请使用字段 $Index;数值类型导出时可取绝对值以及设置小数位;导出为否时,不使用行信息;红色标题导出时列头文字为红色。</div>
            <div style={{color: '#959595', fontSize: '13px', paddingLeft: '10px'}}>如需导出序号,请使用字段 $Index;导出为否时,不使用行信息(文本为空,数值为0);红色标题导出时列头文字为红色。</div>
            <EditTable actions={['edit', 'move', 'copy', 'del']} type="excelcolumn" searchKey={searchKey} wrappedComponentRef={(inst) => this.columnRef = inst} data={verify.columns} columns={excelColumns} onChange={this.changeColumns}/>
          </TabPane>
          {card.intertype === 'system' ? <TabPane tab={
@@ -1159,6 +1207,18 @@
            </Form>
          </TabPane>
        </Tabs>
        <Modal
          title=""
          visible={visible}
          width={1000}
          maskClosable={false}
          closable={false}
          onOk={this.lineSubmit}
          onCancel={() => {this.setState({visible: false, line: null})}}
          destroyOnClose
        >
          <OtherForm line={line} submit={this.lineSubmit} onChange={(values) => this.setState({line: values})}/>
        </Modal>
      </div>
    )
  }
src/templates/sharecomponent/actioncomponent/verifyexcelout/index.scss
@@ -96,6 +96,15 @@
    padding: 0 5px;
    cursor: pointer;
  }
  .edit-other {
    opacity: 0;
    color: #1890ff;
    padding: 0 5px;
    transition: opacity 0.2s;
  }
  tr:not([draggable="false"]) td:hover .edit-other {
    opacity: 1;
  }
}
.verify-excelout-box-tab {
  >.ant-spin {
src/templates/sharecomponent/actioncomponent/verifyexcelout/otherform/index.jsx
New file
@@ -0,0 +1,132 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Radio, Tooltip, Select, Input } from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
// import './index.scss'
class ExcelOutOtherColumn extends Component {
  static propTpyes = {
    onChange: PropTypes.func
  }
  onChange = (key, val) => {
    const { line } = this.props
    this.props.onChange({...line, [key]: val})
  }
  render() {
    const { getFieldDecorator } = this.props.form
    const { line } = this.props
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout} style={{minHeight: '100px', paddingTop: '10px'}}>
        <Row gutter={24}>
          {line.type === 'number' ? <Col span={8}>
            <Form.Item label="取绝对值">
              {getFieldDecorator('abs', {
                initialValue: line.abs || 'false'
              })(
                <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => {this.onChange('abs', e.target.value)}}>
                  <Radio value="true">是</Radio>
                  <Radio value="false">否</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null}
          {line.type === 'text' ? <Col span={8}>
            <Form.Item label="自动换行">
              {getFieldDecorator('wrapText', {
                initialValue: line.wrapText || 'false'
              })(
                <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => {this.onChange('wrapText', e.target.value)}}>
                  <Radio value="true">是</Radio>
                  <Radio value="false">否</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null}
          <Col span={8}>
            <Form.Item label={line.type === 'number' ? '0值' :
              <Tooltip placement="topLeft" title="时间小于 1949-10-02 时。">
                <QuestionCircleOutlined className="mk-form-tip" />
                空值
              </Tooltip>
            }>
              {getFieldDecorator('noValue', {
                initialValue: line.noValue || 'true'
              })(
                <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => {this.onChange('noValue', e.target.value)}}>
                  <Radio value="true">导出</Radio>
                  <Radio value="false">不导出</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
          {line.type === 'number' ? <Col span={8}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="使用格式化时,需设置小数位。">
                <QuestionCircleOutlined className="mk-form-tip" />
                格式化
              </Tooltip>
            }>
              {getFieldDecorator('format', {
                initialValue: line.format || ''
              })(
                <Select onChange={(val) => this.onChange('format', val)}>
                  <Select.Option value=""> 无 </Select.Option>
                  <Select.Option value="thdSeparator"> 千分位 </Select.Option>
                  <Select.Option value="thdSepPm"> 千分位(负值红色) </Select.Option>
                  <Select.Option value="percent"> 百分比 </Select.Option>
                </Select>
              )}
            </Form.Item>
          </Col> : null}
          {line.type === 'text' ? <Col span={8}>
            <Form.Item label="格式化">
              {getFieldDecorator('textFormat', {
                initialValue: line.textFormat || ''
              })(
                <Select onChange={(val) => this.onChange('textFormat', val)}>
                  <Select.Option value=""> 无 </Select.Option>
                  <Select.Option value="YYYY-MM-DD"> YYYY-MM-DD </Select.Option>
                  <Select.Option value="YYYY-MM-DD HH:mm:ss"> YYYY-MM-DD HH:mm:ss </Select.Option>
                </Select>
              )}
            </Form.Item>
          </Col> : null}
          <Col span={8}>
            <Form.Item label="前缀">
              {getFieldDecorator('prefix', {
                initialValue: line.prefix || ''
              })(
                <Input autoComplete="off" onChange={(e) => this.onChange('prefix', e.target.value)} onPressEnter={this.props.submit}/>
              )}
            </Form.Item>
          </Col>
          <Col span={8}>
            <Form.Item label="后缀">
              {getFieldDecorator('postfix', {
                initialValue: line.postfix || ''
              })(
                <Input autoComplete="off" onChange={(e) => this.onChange('postfix', e.target.value)} onPressEnter={this.props.submit}/>
              )}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
  }
}
export default Form.create()(ExcelOutOtherColumn)
src/templates/sharecomponent/actioncomponent/verifyexcelout/otherform/index.scss
src/templates/sharecomponent/actioncomponent/verifyexcelout/utils.jsx
@@ -15,9 +15,9 @@
    let arr_field = []
    verify.columns.forEach(item => {
      if (item.Column !== '$Index') {
        arr_field.push(item.Column)
      }
      if (item.output === 'false' || item.Column === '$Index') return
      arr_field.push(item.Column)
    })
    arr_field = arr_field.join(',')
src/templates/sharecomponent/actioncomponent/verifymegvii/index.scss
@@ -9,11 +9,6 @@
    word-wrap: break-word;
    word-break: break-word;
  }
  .excel-custom-table {
    position: relative;
    top: -25px;
  }
  .errorval {
    display: inline-block;
    width: 30px;
src/templates/sharecomponent/actioncomponent/verifyprint/index.jsx
@@ -97,12 +97,12 @@
        dataIndex: 'operation',
        render: (text, record) =>
          (<div style={{textAlign: 'center'}}>
            <span className="operation-btn" title="编辑" onClick={() => this.handleEdit(record, 'scripts')} style={{color: '#1890ff'}}><EditOutlined /></span>
            <span className="operation-btn" title="状态切换" onClick={() => this.handleStatus(record, 'scripts')} style={{color: '#8E44AD'}}><SwapOutlined /></span>
            <span className="operation-btn" title="编辑" onClick={() => this.handleEdit(record)} style={{color: '#1890ff'}}><EditOutlined /></span>
            <span className="operation-btn" title="状态切换" onClick={() => this.handleStatus(record)} style={{color: '#8E44AD'}}><SwapOutlined /></span>
            <Popconfirm
              overlayClassName="popover-confirm"
              title="确定删除吗?"
              onConfirm={() => this.handleDelete(record, 'scripts')
              onConfirm={() => this.handleDelete(record)
            }>
              <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
            </Popconfirm>
@@ -333,14 +333,9 @@
    })
  }
  handleEdit = (record, type) => {
    let node = null
    if (type === 'scripts') {
      this.scriptsForm.edit(record)
      node = document.getElementById('mk-exout-script')
    }
  handleEdit = (record) => {
    this.scriptsForm.edit(record)
    let node = document.getElementById('mk-exout-script')
    if (node && node.scrollTop) {
      let inter = Math.ceil(node.scrollTop / 10)
@@ -356,6 +351,33 @@
    }
  }
  handleStatus = (record) => {
    let verify = fromJS(this.state.verify).toJS()
    record.status = record.status === 'false' ? 'true' : 'false'
    verify.scripts = verify.scripts.map(item => {
      if (item.uuid === record.uuid) {
        return record
      } else {
        return item
      }
    })
    this.setState({
      verify: verify
    })
  }
  handleDelete = (record) => {
    let verify = fromJS(this.state.verify).toJS()
    verify.scripts = verify.scripts.filter(item => item.uuid !== record.uuid)
    this.setState({ verify: verify })
    this.scriptsForm.check(record)
  }
  showError = (errorType) => {
    if (errorType === 'S') {
      notification.success({
src/templates/sharecomponent/actioncomponent/verifyprint/utils.jsx
@@ -34,8 +34,19 @@
      _dataresource = '(' + _dataresource + ') tb'
    }
    let custompage = /@pageSize@|@orderBy@/i.test(_dataresource + _customScript)
    // 正则替换
    let regoptions = [{
      reg: new RegExp('@orderBy@', 'ig'),
      value: setting.order || ''
    }, {
      reg: new RegExp('@pageSize@', 'ig'),
      value: 10
    }, {
      reg: new RegExp('@pageIndex@', 'ig'),
      value: 1
    }, {
      reg: /@datam@/ig,
      value: `''`
    }, {
@@ -61,10 +72,10 @@
    // 数据源处理, 存在显示列时 
    if (_dataresource) {
      if (setting.order) {
        _dataresource = `/*system_query*/select ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource}) tmptable order by tmptable.rows`
      } else {
      if (custompage || !setting.order) {
        _dataresource = `/*system_query*/select ${arr_field} from ${_dataresource}`
      } else {
        _dataresource = `/*system_query*/select ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource}) tmptable order by tmptable.rows`
      }
    }
src/templates/sharecomponent/searchcomponent/dragsearch/card.jsx
@@ -120,11 +120,11 @@
    if (options.length === 0) {
      options = [{Value: '1', Text: '选项1'}, {Value: '2', Text: '选项2'}]
    }
    formItem = (<Radio.Group value={card.initval} style={{lineHeight: '36px'}}>
    formItem = (<Radio.Group value={card.initval} style={{lineHeight: '36px', whiteSpace: 'nowrap'}}>
      {options.map((item, i) => (<Radio key={i} value={item.Value}> {item.Text} </Radio>))}
    </Radio.Group>)
  } else if (card.type === 'check') {
    formItem = <Checkbox style={{lineHeight: '36px'}} checked={card.initval === card.openVal}>{card.checkTip || ''}</Checkbox>
    formItem = <Checkbox style={{lineHeight: '36px', whiteSpace: 'nowrap'}} checked={card.initval === card.openVal}>{card.checkTip || ''}</Checkbox>
  } else if (card.type === 'range') {
    type = 'range-wrap'
    let vals = card.initval.split(',')
src/templates/sharecomponent/searchcomponent/index.jsx
@@ -93,6 +93,7 @@
   * @description 搜索条件编辑,获取搜索条件表单信息
   */
  handleSearch = (card) => {
    const { config } = this.props
    const { searchlist } = this.state
    let linkableFields = []
@@ -108,8 +109,8 @@
    })
    let columns = null
    if (this.props.config.type === 'table') {
      columns = this.props.config.columns.map(item => {
    if (config.columns && config.columns.length) {
      columns = config.columns.map(item => {
        return {key: item.uuid, text: item.field, value: item.field, label: item.label}
      })
    }
src/templates/zshare/customscript/index.jsx
@@ -328,7 +328,7 @@
            </Col> : null}
            <Col span={16}>
              <Form.Item label={'报错字段'} style={{margin: 0}}>
                ErrorCode, retmsg
                errorcode, retmsg
              </Form.Item>
            </Col>
            <Col span={24} className="sqlfield">
src/templates/zshare/formconfig.jsx
@@ -1869,6 +1869,21 @@
    },
    {
      type: 'radio',
      key: 'format',
      label: '格式化',
      tooltip: '使用千分位时,数值将以千分位格式显示,提交时为原数值。',
      initVal: card.format || '',
      forbid: appType === 'mob',
      options: [{
        value: '',
        text: '无'
      }, {
        value: 'thdSeparator',
        text: '千分位'
      }]
    },
    {
      type: 'radio',
      key: 'colorType',
      label: '颜色类型',
      initVal: card.colorType || 'hex',
@@ -2571,7 +2586,7 @@
      key: 'enter',
      label: '回车事件',
      initVal: (card.type === 'text' || card.type === 'number') ? (card.enter || 'sub') : (card.enter || 'false'),
      tooltip: '1、点击Enter键或文本类表单输入回车符;2、下拉选择或开关的选项切换。',
      tooltip: '1、点击Enter键或文本类表单输入回车符;2、下拉选择、时间、开关的选项切换。',
      options: [{
        value: 'sub',
        text: '提交'
src/templates/zshare/modalform/index.jsx
@@ -21,7 +21,7 @@
const modalTypeOptions = {
  text: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'regular', 'interception', 'span', 'labelwidth', 'encryption', 'tooltip', 'extra', 'enter', 'cursor', 'scan', 'splitline', 'placeholder', 'place', 'marginTop', 'marginBottom', 'lenControl', 'inputType', 'constant', 'mkfocus'],
  number: ['initval', 'readonly', 'hidden', 'decimal', 'min', 'max', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'enter', 'cursor', 'splitline', 'place', 'marginTop', 'marginBottom', 'mkfocus'],
  number: ['initval', 'readonly', 'hidden', 'decimal', 'min', 'max', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'enter', 'cursor', 'splitline', 'place', 'marginTop', 'marginBottom', 'mkfocus', 'format'],
  select: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'declare', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'place', 'enter', 'splitline', 'dropdown', 'marginTop', 'marginBottom', 'pickerMode'],
  checkbox: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'fieldlength', 'span', 'labelwidth', 'tooltip', 'extra', 'place', 'splitline', 'arrange', 'marginTop', 'marginBottom'],
  radio: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'declare', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'place', 'splitline', 'arrange', 'marginTop', 'marginBottom'],
@@ -31,8 +31,8 @@
  fileupload: ['readonly', 'required', 'hidden', 'readin', 'fieldlength', 'maxfile', 'fileType', 'span', 'labelwidth', 'linkSubField', 'tooltip', 'extra', 'compress', 'miniSet', 'splitline', 'marginTop', 'marginBottom', 'maxSize'],
  switch: ['initval', 'openVal', 'closeVal', 'openText', 'closeText', 'readonly', 'hidden', 'readin', 'span', 'labelwidth', 'linkSubField', 'tooltip', 'extra', 'enter', 'splitline', 'marginTop', 'marginBottom'],
  check: ['initval', 'openVal', 'closeVal', 'readonly', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'enter', 'splitline', 'marginTop', 'marginBottom', 'checkTip'],
  date: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'declareType', 'mode', 'splitline', 'place', 'marginTop', 'marginBottom', 'minDate', 'maxDate', 'precision'],
  datemonth: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'splitline', 'place', 'marginTop', 'marginBottom'],
  date: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'enter', 'declareType', 'mode', 'splitline', 'place', 'marginTop', 'marginBottom', 'minDate', 'maxDate', 'precision'],
  datemonth: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'enter', 'splitline', 'place', 'marginTop', 'marginBottom'],
  // datetime: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'declareType', 'mode', 'splitline', 'marginTop', 'marginBottom', 'minDate', 'maxDate'],
  textarea: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'span', 'labelwidth', 'maxRows', 'encryption', 'interception', 'tooltip', 'extra', 'place', 'count', 'placeholder', 'marginTop', 'marginBottom'],
  cascader: ['readonly', 'required', 'hidden', 'readin', 'resourceType', 'fieldlength', 'span', 'labelwidth', 'tooltip', 'extra', 'place', 'splitline', 'marginTop', 'marginBottom', 'separator'],
@@ -243,6 +243,9 @@
        shows.push('unchecked')
      }
    } else if (['date', 'datemonth'].includes(type)) {
      if (this.record.enter === 'tab' || this.record.enter === 'sub') {
        shows.push('tabField')
      }
      reOptions.initval = dateOptions[type]
      reTypes.initval = 'select'
      if (type === 'date') {
src/templates/zshare/pasteform/index.jsx
@@ -116,7 +116,7 @@
      <Form {...formItemLayout} className="config-paste-form">
        <Row gutter={24}>
          <Col span={24}>
            <Form.Item label="配置信息" className="textarea">
            <Form.Item label="" className="textarea">
              {getFieldDecorator('config', {
                initialValue: '',
                rules: [
src/templates/zshare/verifycard/callbackcustomscript/index.jsx
@@ -222,7 +222,7 @@
          </Col> : null}
          {!type ? <Col span={10}>
            <Form.Item label="报错字段" style={{margin: 0, whiteSpace: 'nowrap'}}>
              ErrorCode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
              errorcode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
            </Form.Item>
          </Col> : null}
          {!type ? <Col span={24} className="sqlfield">
src/templates/zshare/verifycard/customscript/index.jsx
@@ -46,7 +46,7 @@
  }
  handleConfirm = () => {
    const { type, workFlow, flowType, useDefaultSql, defaultsql } = this.props
    const { type, workFlow, flowType, flowSql, useDefaultSql, defaultsql } = this.props
    const { editItem, skip } = this.state
    // 表单提交时检查输入值是否正确
    this.props.form.validateFieldsAndScroll((err, values) => {
@@ -136,6 +136,49 @@
          `
        }
        if (window.GLOB.process && workFlow === 'true' && flowSql === 'true') {
          if (flowType === 'start') {
            sql += `
              /* 工作流默认sql */
              insert into s_my_works_flow (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,work_group,works_flow_detail_id,work_grade,bid,createuserid,CreateUser,CreateStaff,upid)
              select @ID@,@works_flow_code@,@works_flow_name@,@works_flow_param@,@status@,@statusname@,@work_group@,@works_flow_detail_id@,@work_grade@,@bid@,@UserID@,@UserName,@FullName,@time_id@
              insert into s_my_works_flow_log (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,works_flow_detail_id,work_group,work_grade,upid)
              select @ID@,@works_flow_code@,@works_flow_name@ ,@works_flow_param@,@status@,@statusname@,@works_flow_detail_id@,@work_group@,@work_grade@,@time_id@
              insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
              select @ID@,@works_flow_code@,@works_flow_detail_id@,@userid@,@start_type@,@userid@,@UserName,@FullName,@time_id@
            `
          } else {
            sql += `
              /* 工作流默认sql */
              update s_my_works_flow set status=@status@,statusname=@statusname@,works_flow_param=@works_flow_param@,works_flow_detail_id=@works_flow_detail_id@,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
              where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
              insert into s_my_works_flow_log (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,works_flow_detail_id,work_group,work_grade,upid)
              select @ID@,@works_flow_code@,@works_flow_name@ ,@works_flow_param@,@status@,@statusname@,@works_flow_detail_id@,@work_group@,@work_grade@,@time_id@
              update s_my_works_flow_role set deleted=10,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
              where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
              if @check_userids@ != ''
              begin
                    insert into s_my_works_flow_role (works_flow_id,works_flow_code,userid,works_flow_detail_id,createuserid,CreateUser,CreateStaff,upid)
                    select @ID@,@works_flow_code@,ID,@works_flow_detail_id@,@userid@,@UserName,@FullName,@time_id@ from dbo.SplitComma(@check_userids@)
                    insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
                    select @ID@,@works_flow_code@,@works_flow_detail_id@,ID,@check_type@,@userid@,@UserName,@FullName,@time_id@ from dbo.SplitComma(@check_userids@)
              end
              if @notice_userids@ != ''
              begin
                    update n
                    set deleted=10,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
                    from (select * from s_my_works_flow_notice where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0) n
                    inner join (select ID from dbo.SplitComma(@notice_userids@)) s
                    on n.userid = s.id
                    insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
                    select @ID@,@works_flow_code@,@works_flow_detail_id@,ID,@notice_type@,@userid@,@UserName,@FullName,@time_id@ from dbo.SplitComma(@notice_userids@)
              end
            `
          }
        }
        sql += _backCustomScript + tail
        if (window.GLOB.funcs && window.GLOB.funcs.length > 0) {
@@ -166,6 +209,9 @@
          if (flowType !== 'start') {
            sql = sql.replace(/@check_userids@/ig, `'checkuserids'`)
            sql = sql.replace(/@notice_userids@/ig, `'noticeuserids'`)
          } else {
            sql = sql.replace(/@check_userids@/ig, `''`)
            sql = sql.replace(/@notice_userids@/ig, `''`)
          }
        }
        
@@ -239,13 +285,16 @@
        insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
        select @ID@,@works_flow_code@,@works_flow_detail_id@,@userid@,@start_type@,@userid@,@UserName,@FullName,@time_id@`
      } else {
        value = `update s_my_works_flow set status=@status@,statusname=@statusname@,works_flow_param=@works_flow_param@,works_flow_detail_id=@works_flow_detail_id@,modifydate=getdate(),modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
        value = `update s_my_works_flow set status=@status@,statusname=@statusname@,works_flow_param=@works_flow_param@,works_flow_detail_id=@works_flow_detail_id@,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
        where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
        insert into s_my_works_flow_log (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,works_flow_detail_id,work_group,work_grade,upid)
        select @ID@,@works_flow_code@,@works_flow_name@ ,@works_flow_param@,@status@,@statusname@,@works_flow_detail_id@,@work_group@,@work_grade@,@time_id@
        update s_my_works_flow_role set deleted=10,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
        where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
        if @check_userids@ != ''
        begin
              delete s_my_works_flow_role where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
              insert into s_my_works_flow_role (works_flow_id,works_flow_code,userid,works_flow_detail_id,createuserid,CreateUser,CreateStaff,upid)
              select @ID@,@works_flow_code@,ID,@works_flow_detail_id@,@userid@,@UserName,@FullName,@time_id@ from dbo.SplitComma(@check_userids@)
              insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
@@ -253,7 +302,8 @@
        end
        if @notice_userids@ != ''
        begin
              delete n
              update n
              set deleted=10,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
              from (select * from s_my_works_flow_notice where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0) n
              inner join (select ID from dbo.SplitComma(@notice_userids@)) s
              on n.userid = s.id
@@ -303,17 +353,17 @@
          </Col> : null}
          {!_type ? <Col span={10}>
            <Form.Item label={'报错字段'} style={{margin: 0, whiteSpace: 'nowrap'}}>
              ErrorCode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
              errorcode(增加后缀NT表示数据不回滚,如ENT、NNT、FNT、NMNT、CNT、-2NT), retmsg
            </Form.Item>
          </Col> : null}
          {!_type ? <Col span={24} className="sqlfield">
            <Form.Item label={'可用字段'}>
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="公共值,请按照@xxx@格式使用。"><span style={{color: '#1890ff'}}>BID, ID, LoginUID, SessionUid, UserID, Appkey, time_id, typename, datam</span></Tooltip>,&nbsp;
              {window.GLOB.process && workFlow === 'true' ? <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="工作流变量,请按照@xxx@格式使用。"><span style={{color: '#26C281'}}>works_flow_code, works_flow_name, works_flow_param, works_flow_detail_id, status, statusname, work_group, work_grade, start_type, check_type, notice_type, check_userids, notice_userids, </span></Tooltip> : null}
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="系统变量,系统会定义变量并赋值。"><span style={{color: '#fa8c16'}}>UserName, FullName, RoleID, mk_departmentcode, mk_organization, mk_user_type, mk_nation, mk_province, mk_city, mk_district, mk_address, mk_deleted</span></Tooltip>,&nbsp;
              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="系统变量,系统会定义变量并在单号生成或创建凭证时使用。"><span style={{color: '#13c2c2'}}>BillCode, BVoucher, FIBVoucherDate, FiYear, ModularDetailCode</span></Tooltip>
              {formfields ? <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="表单变量,系统会定义变量并赋值。">, <span style={{color: '#8E44AD'}}>{formfields}</span></Tooltip> : ''}
              {colfields ? <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="字段集变量,系统会定义变量并赋值。">, {colfields}</Tooltip> : ''}
              {window.GLOB.process && workFlow === 'true' ? <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title="工作流变量,请按照@xxx@格式使用。注:check_userids、notice_userids 在审批或驳回时有效。">,<span style={{color: 'purple'}}> works_flow_code, works_flow_name, works_flow_param, works_flow_detail_id, status, statusname, work_group, work_grade, start_type, check_type, notice_type, check_userids, notice_userids</span></Tooltip> : null}
            </Form.Item>
          </Col> : null}
          {!_type ? <Col span={8} style={{whiteSpace: 'nowrap'}}>
@@ -367,7 +417,7 @@
          </Col>
          <Col span={24} className="sql">
            <Form.Item label={
              <Tooltip placement="topLeft" overlayStyle={{width: '320px', maxWidth: '320px'}} title={<><div>{'调试替换符 /*$breakpoint_begin_xxxx@ 、@breakpoint_end_xxxx$*/,在控制台中输入 window.debug = \'xxxx\' 会启用对应的调试语句,快捷键 ctrl+c 或在控制台中输入 window.debug = false 关闭调试。注:调试时字符 $breakpoint_proc@ 将被替换。'}</div><div style={{height: '5px'}}></div><div>{'数据检查替换符 $check@ -> \'\'、 @check$ -> \'\',ErrorCode等于C时会询问是否继续执行,确定时 $check@ -> /*、 @check$ -> */。注:1、需使用系统接口 2、行设置为“选择多行”时无效。'}</div></>}>
              <Tooltip placement="topLeft" overlayStyle={{width: '520px', maxWidth: '520px'}} title={<><div>{`调试替换符 /*$breakpoint_begin_xxxx@ 、@breakpoint_end_xxxx$*/,在控制台中输入 window.debug = 'xxxx' 会启用对应的调试语句,快捷键 ctrl+c 或在控制台中输入 window.debug = false 关闭调试。注:调试时字符 $breakpoint_proc@ 将被替换。`}</div><div style={{height: '5px'}}></div><div>{`数据检查替换符 $check@ -> ''、 @check$ -> '',ErrorCode等于C时会询问是否继续执行,确定时 $check@ -> /*、 @check$ -> */。注:1、需使用系统接口 2、行设置为“选择多行”时无效。`}</div><div style={{height: '5px'}}></div><div>{`数据权限替换符 $@ -> /* 或 ''、 @$ -> */ 或 ''`}</div></>}>
                <QuestionCircleOutlined className="mk-form-tip" />
                sql
              </Tooltip>
src/templates/zshare/verifycard/index.jsx
@@ -1777,6 +1777,9 @@
                type="fullscreen"
                btn={this.props.card}
                initsql={this.state.initsql}
                workFlow={verify.workFlow}
                flowType={verify.flowType}
                flowSql={verify.flowSql}
                customScripts={verify.scripts}
                useDefaultSql={verify.default !== 'false'}
                defaultsql={this.state.defaultsql}
@@ -1792,6 +1795,7 @@
              initsql={this.state.initsql}
              workFlow={verify.workFlow}
              flowType={verify.flowType}
              flowSql={verify.flowSql}
              customScripts={verify.scripts}
              useDefaultSql={verify.default !== 'false'}
              defaultsql={this.state.defaultsql}
src/utils/utils-custom.js
@@ -193,6 +193,42 @@
  }
  /**
   * @description 获取指定组件
   * @return {String}  组件id
   */
  static getComponent (Id) {
    let interfaces = window.GLOB.customMenu.interfaces
    let components = window.GLOB.customMenu.components
    let cell = null
    let mapComponents = (components = []) => {
      components.forEach(item => {
        if (item.uuid === Id) {
          cell = item
        }else if (item.type === 'tabs') {
          item.subtabs.forEach(f_tab => {
            mapComponents(f_tab.components)
          })
        } else if (item.type === 'group') {
          mapComponents(item.components)
        }
      })
    }
    mapComponents(components)
    if (!cell && interfaces) {
      interfaces.forEach(m => {
        if (m.uuid === Id && m.status === 'true') {
          cell = m
        }
      })
    }
    return cell
  }
  /**
   * @description 获取上级模块
   * @return {String}  selfId  当前组件id
   */
@@ -540,7 +576,16 @@
            return cell
          })
        }
      } else if (item.type === 'table' && item.cols) {
      } else if (item.type === 'table') {
        if (item.supNodes && item.supNodes.length > 0) {
          item.supNodes = item.supNodes.map(cell => {
            cell.nodes = cell.nodes.map(n => md5(commonId + n))
            cell.componentId = cell.nodes[cell.nodes.length - 1]
            return cell
          })
        }
        let loopCol = (cols) => {
          return cols.map(col => {
            if (col.type === 'action') {
@@ -578,7 +623,7 @@
          })
        }
        item.cols = loopCol(item.cols)
        item.cols = loopCol(item.cols || [])
        if (item.colsCtrls) {
          item.colsCtrls = item.colsCtrls.map(col => {
@@ -846,7 +891,7 @@
          return cell
        })
      }
    } else if (item.type === 'table' && item.cols) {
    } else if (item.type === 'table') {
      let loopCol = (cols) => {
        return cols.map(col => {
          if (col.type === 'action') {
@@ -887,7 +932,7 @@
        })
      }
      item.cols = loopCol(item.cols)
      item.cols = loopCol(item.cols || [])
      if (item.colsCtrls) {
        item.colsCtrls = item.colsCtrls.map(col => {
@@ -1009,7 +1054,7 @@
      } else if (item.type === 'daterange') {
        value = '1949-10-01 00:00:00.000,1949-10-02 00:00:00.000'
      } else if (item.type === 'range') {
        value = item.initval || `${item.minValue || '-999999'},${item.maxValue || '999999'}`
        value = item.initval || `${item.minValue || '-999999999'},${item.maxValue || '999999999'}`
      } else if (item.type === 'multiselect' || (item.type === 'checkcard' && item.multiple === 'true')) {
        type = 'multi'
      }
@@ -1058,7 +1103,7 @@
      searchText.push('(' + item.key + ' >= \'' + val[0] + '\' AND ' + item.key + ' < \'' + val[1] + '\')')
    } else if (item.type === 'range') {
      let val = item.value.split(',')
      searchText.push('(' + item.key + ' >= ' + (val[0] || -999999) + ' AND ' + item.key + ' < ' + (val[1] || 999999) + ')')
      searchText.push('(' + item.key + ' >= ' + (val[0] || -999999999) + ' AND ' + item.key + ' <= ' + (val[1] || 999999999) + ')')
    } else if (item.type === 'datemonth') {
      if (item.match === '=') {
        searchText.push('(' + item.key + ' = \'' + item.value + '\')')
@@ -1122,10 +1167,10 @@
      let val = item.value.split(',')
      options.push({
        reg: new RegExp('@' + item.key + '@', 'ig'),
        value: `${val[0] || -999999}`
        value: `${val[0] || -999999999}`
      }, {
        reg: new RegExp('@' + item.key + '1@', 'ig'),
        value: `${val[1] || 999999}`
        value: `${val[1] || 999999999}`
      })
    } else if (item.type === 'datemonth') {
      if (item.match === '=') {
@@ -1675,7 +1720,7 @@
      } else if (!card.setting.supModule) {
        errors.push({ level: 0, detail: '未设置上级组件!'})
      }
    } else if (card.type === 'card' && card.subtype === 'datacard') { // 数据卡,可能有多上级
    } else if ((card.type === 'card' && card.subtype === 'datacard') || card.subtype === 'normaltable') { // 数据卡、table,可能有多上级
      if (card.wrap.supType !== 'multi' && !card.setting.supModule) {
        errors.push({ level: 0, detail: '未设置上级组件!'})
      }
src/utils/utils-datamanage.js
@@ -1,16 +1,13 @@
import md5 from 'md5'
import moment from 'moment'
import { notification, Modal } from 'antd'
import MKEmitter from '@/utils/events.js'
import Utils from './utils.js'
export default class DataUtils {
  /**
   * @description 数据源名称,用于统一查询
   * @param {Object}   setting      数据源设置
   * @param {Array}    search       搜索条件
   * @param {String}   orderBy      排序方式
   * @param {Number}   pageIndex    页码
   * @param {Number}   pageSize     每页数量
   * @param {String}   BID          上级ID
   * @description 数据源统一查询
   */
  static getQueryDataParams (setting, search = [], orderBy = '', pageIndex = 1, pageSize = 10, BID, id, year) {
    let param = null
@@ -442,6 +439,46 @@
    return param
  }
  /**
   * @description 数据获取成功
   */
  static querySuccess (result) {
    if (!result.message) return
    if (result.ErrCode === 'Y') {
      Modal.success({
        title: result.message
      })
    } else if (result.ErrCode === 'S') {
      notification.success({
        top: 92,
        message: result.message,
        duration: 2
      })
    }
  }
  /**
   * @description 数据获取失败
   */
  static queryFail (result) {
    if (!result.message && result.ErrCode !== 'version_up') return
    if (result.ErrCode === 'N') {
      Modal.error({
        title: result.message,
      })
    } else if (result.ErrCode === 'version_up') {
      MKEmitter.emit('reloadTabs')
    } else if (result.ErrCode !== '-2') {
      notification.error({
        top: 92,
        message: result.message,
        duration: 10
      })
    }
  }
}
/**
src/utils/utils.js
@@ -204,6 +204,13 @@
      values = window.GLOB.SearchBox.get(config.$searchId + 'cache')
    }
    let supModule = ''
    if (config.setting && config.setting.supModule) {
      supModule = config.setting.supModule
    } else if (config.wrap && config.wrap.supModule) {
      supModule = config.wrap.supModule
    }
    config.search = config.search.map(item => {
      item.hidden = item.Hide === 'true'
      item.required = !item.hidden && item.required === 'true'
@@ -234,12 +241,14 @@
          } else {
            item.$initval = item.initval
            item.$supId = config.$pageId
            if (config.setting && config.setting.supModule) {
              item.$supId = config.setting.supModule
            if (supModule) {
              item.$supId = supModule
              config.checkBid = true
              config.setting.checkBid = true
              item.checkShift = true
              if (config.setting) {
                config.setting.checkBid = true
              }
            }
            item.initval = ''
@@ -296,12 +305,14 @@
          } else {
            item.$initval = item.initval
            item.$supId = config.$pageId
            if (config.setting && config.setting.supModule) {
              item.$supId = config.setting.supModule
            if (supModule) {
              item.$supId = supModule
              config.checkBid = true
              config.setting.checkBid = true
              item.checkShift = true
              if (config.setting) {
                config.setting.checkBid = true
              }
            }
            item.initval = ''
@@ -393,10 +404,12 @@
          item.initType = ''
        }
      } else if ((item.type === 'select' || item.type === 'link') && item.resourceType === '1') {
        if (/@BID@/ig.test(item.dataSource) && config.setting && config.setting.supModule) {
        if (/@BID@/ig.test(item.dataSource) && supModule) {
          config.checkBid = true
          config.setting.checkBid = true
          item.checkBid = true
          if (config.setting) {
            config.setting.checkBid = true
          }
        }
        if (item.initval === '$first') {
          item.initval = ''
@@ -541,8 +554,8 @@
      } else if (item.type === 'range') {
        let val = item.value.split(',')
        newsearches[item.key] = val[0] || -999999
        newsearches[item.key + '1'] = val[1] || 999999
        newsearches[item.key] = val[0] || -999999999
        newsearches[item.key + '1'] = val[1] || 999999999
      } else if (item.type === 'dateweek') {
        let _startval = ''
        let _endval = ''
@@ -690,7 +703,7 @@
      } else if (item.type === 'range') {
        let val = item.value.split(',')
        searchText.push('(' + item.key + ' >= ' + (val[0] || -999999) + ' AND ' + item.key + ' < ' + (val[1] || 999999) + ')')
        searchText.push('(' + item.key + ' >= ' + (val[0] || -999999999) + ' AND ' + item.key + ' <= ' + (val[1] || 999999999) + ')')
      } else {
        searchText.push('(' + item.key + ' ' + item.match + ' \'' + item.value + '\')')
      }
@@ -766,9 +779,9 @@
        let copy = JSON.parse(JSON.stringify(item))
        copy.key = copy.key + '1'
        copy.value = val[1] || 999999
        copy.value = val[1] || 999999999
        
        item.value = val[0] || -999999
        item.value = val[0] || -999999999
        options.push(item)
        options.push(copy)
@@ -1132,6 +1145,13 @@
    let _uniquesql = ''
    if (btn.uniques && btn.uniques.length > 0) {
      let textFields = []
      btn.columns.forEach((col) => {
        if (/^Nvarchar/ig.test(col.type)) {
          textFields.push(col.Column)
        }
      })
      btn.uniques.forEach(unique => {
        if (unique.status === 'false' || !unique.verifyType) return
@@ -1140,6 +1160,14 @@
        let _afields = _fields.map(_field => `a.${_field}`)
        _fields_ = _fields_.join(' and ')
        let _where = []
        _fields.forEach(f => {
          if (textFields.includes(f)) {
            _where.push(`${f}!=''`)
          }
        })
        _where = _where.length ? `where ${_where.join(' and ')} ` : ''
        if (unique.verifyType === 'logic' || unique.verifyType === 'logic_temp') {
          _fields_ += ' and b.deleted=0'
        }
@@ -1147,7 +1175,7 @@
        _uniquesql += `
      /* 重复性验证 */
      Set @tbid=''
      Select top 1 @tbid=${_fields.join('+\' \'+')} from (select 1 as n,${unique.field} from #${sheet} ) a group by ${unique.field} having sum(n)>1
      Select top 1 @tbid=${_fields.join('+\' \'+')} from (select 1 as n,${unique.field} from #${sheet} ${_where}) a group by ${unique.field} having sum(n)>1
      
      If @tbid!=''
      Begin
@@ -1156,7 +1184,7 @@
      end
      
      ${unique.verifyType.indexOf('temp') === -1 ? `Set @tbid=''
      Select top 1 @tbid=${_afields.join('+\' \'+')} from  #${sheet} a Inner join ${database}${sheet} b on ${_fields_}
      Select top 1 @tbid=${_afields.join('+\' \'+')} from ${_where ? `(select * from #${sheet} ${_where})` : `#${sheet}`} a Inner join ${database}${sheet} b on ${_fields_}
      
      If @tbid!=''
      Begin
@@ -2122,6 +2150,7 @@
    let statusName = ''
    let detailId = ''
    let error = ''
    let userid = sessionStorage.getItem('UserID') || ''
    if (verify.flowType === 'start') {
      target = flow.cells.filter(cell => cell.mknode === 'start')[0]
@@ -2250,7 +2279,15 @@
      error = '行信息中无工作流参数'
    }
    if (verify.flowSql === 'true' && target) {
    if (error) {
      status = 0
      statusName = '异常'
      _sql += `
      /* 工作流异常sql */
      select @ErrorCode='E',@retmsg='${error}' goto aaa
      `
    } else if (verify.flowSql === 'true' && target) {
      if (verify.flowType === 'start') {
        _sql += `
      /* 工作流默认sql */
@@ -2261,16 +2298,124 @@
      insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
      select @ID@,@works_flow_code@,@works_flow_detail_id@,@userid@,@start_type@,@userid@,@UserName,@FullName,@time_id@
      `
      } else if (line && line.approvalMethod === 'countersign' && (!node.checkIds || !node.checkIds.includes(userid))) {
        status = 0
        statusName = '异常'
        _sql += `
        /* 工作流异常sql */
        select @ErrorCode='E',@retmsg='当前用户不在审批人列表中' goto aaa
        `
      } else if (line && line.approvalMethod === 'countersign' && node.checkIds.length > 1) {
        let label = ''
        let field = ''
        let mark = line.mark || '已审核'
        let _declare = []
        let _select = []
        let _select2 = []
        let _where = []
        let fields = ['statuscharone', 'statuschartwo', 'statuscharthree', 'statuscharfour', 'statuscharfive']
        node.checkUsers.forEach((user, index) => {
          if (user.worker_id === userid) {
            field = fields[index]
            label = `'${user.parentNames[2] || ''}${user.workername || ''}${mark}'`
          } else {
            _declare.push(`@works_flow_${fields[index]} nvarchar(50)`)
            _select.push(`@works_flow_${fields[index]}=''`)
            _select2.push(`@works_flow_${fields[index]}=${fields[index]}`)
            _where.push(`@works_flow_${fields[index]}!=''`)
          }
        })
        _declare = _declare.join(',')
        _select = _select.join(',')
        _select2 = _select2.join(',')
        _where = _where.join(' and ')
        _sql += `
      /* 工作流默认sql */
      declare ${_declare},@works_flow_key_id nvarchar(50),@works_flow_key_status nvarchar(20),@s_my_works_flow_log_param  nvarchar(max),@s_my_works_flow_log_status int,@s_my_works_flow_log_statusname nvarchar(50),@s_my_works_flow_log_detail_id  nvarchar(50)
      select ${_select},@works_flow_key_id='',@works_flow_key_status ='',@s_my_works_flow_log_param='',@s_my_works_flow_log_status=0,@s_my_works_flow_log_statusname='',@s_my_works_flow_log_detail_id=''
      select ${_select2},@works_flow_key_id=id,@s_my_works_flow_log_param=works_flow_param,@s_my_works_flow_log_status=status,@s_my_works_flow_log_statusname=statusname,@s_my_works_flow_log_detail_id=works_flow_detail_id
      from s_my_works_flow where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
      if ${_where}
      begin
          set @works_flow_key_status='Y'
      end
      if @works_flow_key_status='Y'
      begin
            update s_my_works_flow set ${field}=${label},status=@status@,statusname=@statusname@,works_flow_param=@works_flow_param@,works_flow_detail_id=@works_flow_detail_id@,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
            where id=@works_flow_key_id
            insert into s_my_works_flow_log (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,works_flow_detail_id,work_group,work_grade,upid,${field})
            select @ID@,@works_flow_code@,@works_flow_name@ ,@works_flow_param@,@status@,@statusname@,@works_flow_detail_id@,@work_group@,@work_grade@,@time_id@,${label}
            update s_my_works_flow_role set deleted=10,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
            where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
            if @check_userids@ != ''
            begin
                  insert into s_my_works_flow_role (works_flow_id,works_flow_code,userid,works_flow_detail_id,createuserid,CreateUser,CreateStaff,upid)
                  select @ID@,@works_flow_code@,ID,@works_flow_detail_id@,@userid@,@UserName,@FullName,@time_id@ from dbo.SplitComma(@check_userids@)
                  insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
                  select @ID@,@works_flow_code@,@works_flow_detail_id@,ID,@check_type@,@userid@,@UserName,@FullName,@time_id@ from dbo.SplitComma(@check_userids@)
            end
            if @notice_userids@ != ''
            begin
                  update n
                  set deleted=10,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
                  from (select * from s_my_works_flow_notice where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0) n
                  inner join (select ID from dbo.SplitComma(@notice_userids@)) s
                  on n.userid = s.id
                  insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
                  select @ID@,@works_flow_code@,@works_flow_detail_id@,ID,@notice_type@,@userid@,@UserName,@FullName,@time_id@ from dbo.SplitComma(@notice_userids@)
            end
      end
      else
      begin
            update s_my_works_flow set ${field}=${label},modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
            where id =@works_flow_key_id
            insert into s_my_works_flow_log (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,works_flow_detail_id,work_group,work_grade,upid,${field})
            select @ID@,@works_flow_code@,@works_flow_name@ ,@s_my_works_flow_log_param,@s_my_works_flow_log_status,@s_my_works_flow_log_statusname,@s_my_works_flow_log_detail_id,@work_group@,@work_grade@,@time_id@,${label}
            update s_my_works_flow_role set deleted=10,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
            where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0 and userid =@userid@
      end
      `
      } else {
        _sql += `
      /* 工作流默认sql */
      update s_my_works_flow set status=@status@,statusname=@statusname@,works_flow_param=@works_flow_param@,works_flow_detail_id=@works_flow_detail_id@,modifydate=getdate(),modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
      set @retmsg =''
      select @retmsg='X' from s_my_works_flow_role where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0 and userid =@userid@
      if @retmsg =''
      begin
          select @retmsg='X' from s_my_works_flow_role where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and userid=@userid@
          if @retmsg !=''
          begin
              select @ErrorCode='E', @retmsg='当前单据已审核,请刷新后重试'
            goto aaa
          end
          select @retmsg='页面数据已更新,或没有当前单据的审批权限'
            goto aaa
      end
      set @retmsg=''
      update s_my_works_flow set status=@status@,statusname=@statusname@,works_flow_param=@works_flow_param@,works_flow_detail_id=@works_flow_detail_id@,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
      where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
      insert into s_my_works_flow_log (works_flow_id,works_flow_code,works_flow_name,works_flow_param,status,statusname,works_flow_detail_id,work_group,work_grade,upid)
      select @ID@,@works_flow_code@,@works_flow_name@ ,@works_flow_param@,@status@,@statusname@,@works_flow_detail_id@,@work_group@,@work_grade@,@time_id@
      update s_my_works_flow_role set deleted=10,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
      where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
      if @check_userids@ != ''
      begin
            delete s_my_works_flow_role where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0
            insert into s_my_works_flow_role (works_flow_id,works_flow_code,userid,works_flow_detail_id,createuserid,CreateUser,CreateStaff,upid)
            select @ID@,@works_flow_code@,ID,@works_flow_detail_id@,@userid@,@UserName,@FullName,@time_id@ from dbo.SplitComma(@check_userids@)
            insert into s_my_works_flow_notice (works_flow_id,works_flow_code,works_flow_detail_id,userid,notice_type,createuserid,CreateUser,CreateStaff,upid)
@@ -2278,7 +2423,8 @@
      end
      if @notice_userids@ != ''
      begin
            delete n
            update n
            set deleted=10,modifydate=getdate(),upid=@time_id@,modifyuserid=@userid@,modifyuser=@username,modifystaff=@fullname
            from (select * from s_my_works_flow_notice where works_flow_id=@ID@ and works_flow_code=@works_flow_code@ and deleted=0) n
            inner join (select ID from dbo.SplitComma(@notice_userids@)) s
            on n.userid = s.id
@@ -2297,12 +2443,27 @@
      `
    }
    let checkIds = []
    let checkUsers = []
    let work_grade = sessionStorage.getItem('work_grade') || 0
    if (verify.flowType !== 'start') {
      if (line) {
        let checkIds = []
        let noticeIds = []
        line.mkdata.members && line.mkdata.members.forEach(item => {
          checkIds.push(item.worker_id)
          if (line.mkdata.approver === 'departmentManager') {
            if (item.job_type === 'manage' && departmentcode === item.parentIds[1]) {
              checkIds.push(item.worker_id)
              checkUsers.push(item)
            }
          } else if (line.mkdata.approver === 'directManager') {
            if (departmentcode === item.parentIds[1] && item.work_grade > work_grade) {
              checkIds.push(item.worker_id)
              checkUsers.push(item)
            }
          } else {
            checkIds.push(item.worker_id)
            checkUsers.push(item)
          }
        })
        line.mkdata.copys && line.mkdata.copys.forEach(item => {
          noticeIds.push(item.worker_id)
@@ -2313,6 +2474,9 @@
        _sql = _sql.replace(/@check_userids@/ig, `''`)
        _sql = _sql.replace(/@notice_userids@/ig, `''`)
      }
    } else {
      _sql = _sql.replace(/@check_userids@/ig, `''`)
      _sql = _sql.replace(/@notice_userids@/ig, `''`)
    }
    _sql = _sql.replace(/@start_type@/ig, `'开始'`)
@@ -2321,9 +2485,10 @@
    _sql = _sql.replace(/@works_flow_code@/ig, `'${flow.flow_code}'`)
    _sql = _sql.replace(/@works_flow_name@/ig, `'${flow.flow_name}'`)
    if (target) {
      let label = target.attrs && target.attrs.text && target.attrs.text.text ? target.attrs.text.text : ''
      let msg = {...target.mkdata, label: label, id: target.id}
      let msg = {...target.mkdata, label: label, id: target.id, checkIds, checkUsers}
      _sql = _sql.replace(/@works_flow_param@/ig, `'${window.btoa(window.encodeURIComponent(JSON.stringify(msg)))}'`)
    } else {
      _sql = _sql.replace(/@works_flow_param@/ig, `''`)
@@ -2332,7 +2497,7 @@
    _sql = _sql.replace(/@status@/ig, `'${status}'`)
    _sql = _sql.replace(/@statusname@/ig, `'${statusName}'`)
    _sql = _sql.replace(/@work_group@/ig, `'${sessionStorage.getItem('work_group') || ''}'`)
    _sql = _sql.replace(/@work_grade@/ig, `'${sessionStorage.getItem('work_grade') || 0}'`)
    _sql = _sql.replace(/@work_grade@/ig, `'${work_grade}'`)
  }
  if (_backCustomScript) {
src/views/appcheck/header/index.jsx
@@ -7,13 +7,16 @@
class AppManageHeader extends Component {
  state = {
    avatar: sessionStorage.getItem('CloudAvatar') || avatar,
    userName: sessionStorage.getItem('CloudUserName')
    userName: sessionStorage.getItem('CloudUserName'),
    logo: sessionStorage.getItem('CloudLogo') || MainLogo
  }
  render () {
    const { logo } = this.state
    return (
      <header className="app-manage-header-container">
        <div className="header-logo"><img src={MainLogo} alt=""/></div>
        <div className="header-logo"><img src={logo} alt=""/></div>
        <div className="title">
          应用管理
        </div>
src/views/appmanage/header/index.jsx
@@ -7,13 +7,16 @@
class AppManageHeader extends Component {
  state = {
    avatar: sessionStorage.getItem('CloudAvatar') || avatar,
    userName: sessionStorage.getItem('CloudUserName')
    userName: sessionStorage.getItem('CloudUserName'),
    logo: sessionStorage.getItem('CloudLogo') || MainLogo
  }
  render () {
    const { logo } = this.state
    return (
      <header className="app-manage-header-container">
        <div className="header-logo"><img src={MainLogo} alt=""/></div>
        <div className="header-logo"><img src={logo} alt=""/></div>
        <div className="title">
          应用管理
        </div>
src/views/billprint/index.jsx
@@ -1,6 +1,6 @@
import React, { Component } from 'react'
import { is, fromJS } from 'immutable'
import { Col, Row, Spin, notification, Button } from 'antd'
import { Col, Row, Spin, notification, Button, Modal } from 'antd'
import moment from 'moment'
import Api from '@/api'
@@ -47,6 +47,8 @@
    auto: true,
    ismob: /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i.test(navigator.userAgent)
  }
  reloading = false
  UNSAFE_componentWillMount() {
    const { params } = this.props.match
@@ -367,7 +369,9 @@
            let getColumns = (cols) => {
              return cols.filter(item => {
                if (item.Hide === 'true') return false
                item.IsSort = 'false'
                if (item.type === 'number') {
                  if (typeof(item.decimal) === 'number') {
                    item.round = Math.pow(10, item.decimal)
@@ -629,6 +633,9 @@
    if (cell.marks && cell.marks.length === 0) {
      cell.marks = null
    }
    if (cell.anchors && cell.anchors.length === 0) {
      cell.anchors = null
    }
    
    if (['text', 'number', 'formula'].includes(cell.eleType)) {
@@ -912,11 +919,15 @@
            if (item.func === 'sPC_Get_structured_data' && /将截断字符串或二进制数据/ig.test(res.message)) {
              res.message = res.message + '请检查字段集'
            }
            notification.warning({
              top: 92,
              message: res.message,
              duration: 5
            })
            if (res.ErrCode === 'version_up') {
              this.reloadTabs()
            } else {
              notification.warning({
                top: 92,
                message: res.message,
                duration: 5
              })
            }
            resolve(false)
          } else {
            res.componentId = componentId
@@ -1076,6 +1087,20 @@
    })
  }
  reloadTabs = () => {
    if (this.reloading) return
    this.reloading = true
    Api.getAppVersion(true).then(() => {
      window.location.reload()
    }, (message) => {
      Modal.error({
        title: message || '系统配置更新失败!',
      })
    })
  }
  canvasToImage(canvas) {
    let image = new Image()
    image.src = canvas.toDataURL('image/jpg')
src/views/design/header/index.jsx
@@ -23,6 +23,7 @@
    menulist: null, // 一级菜单
    userName: sessionStorage.getItem('CloudUserName'),
    avatar: Utils.getrealurl(sessionStorage.getItem('CloudAvatar')),
    logo: sessionStorage.getItem('CloudLogo') || MainLogo,
    visible: false,
    loading: false
  }
@@ -408,11 +409,11 @@
  render () {
    const { mainMenu, editLevel } = this.props
    const { menulist, visible, loading } = this.state
    const { menulist, visible, loading, logo } = this.state
    return (
      <header className={'sys-header-container ant-menu-dark ' + (['level2', 'level3'].includes(editLevel) ? 'mask' : '')} id="main-header-container">
        <div className="header-logo"><img src={MainLogo} alt=""/></div>
        <div className="header-logo"><img src={logo} alt=""/></div>
        <div className="header-collapse">
          <MenuFoldOutlined/>
        </div>
src/views/interface/header/index.jsx
@@ -9,6 +9,7 @@
  state = {
    userName: sessionStorage.getItem('CloudUserName'),
    avatar: Utils.getrealurl(sessionStorage.getItem('CloudAvatar')),
    logo: sessionStorage.getItem('CloudLogo') || MainLogo
  }
  UNSAFE_componentWillMount() {}
@@ -18,10 +19,11 @@
  }
  render () {
    const { logo } = this.state
    return (
      <header className="interface-header-container">
        <div className="header-logo"><img src={MainLogo} alt=""/></div>
        <div className="header-logo"><img src={logo} alt=""/></div>
        <div className="title">接口调试</div>
        <div className="header-setting">
          <span className="close" onClick={this.close}>关闭</span>
src/views/login/index.jsx
@@ -41,13 +41,6 @@
    syncing: false,
  }
  changelang (item) {
    // 切换语言
    this.setState({
      dict: item === 'zh-CN' ? zhCN : enUS
    })
  }
  handleSubmit = () => {
    this.loginformRef.handleConfirm().then(res => {
      this.setState({
@@ -343,6 +336,7 @@
    authCode = authCode ? authCode.split(',') : []
    let index = authCode.findIndex(key => key === _s)
    let license = false
    let appkey = window.GLOB.localkey || window.GLOB.appkey
    if (window.GLOB.licenseKey) {
      if (window.GLOB.licenseKey.length !== 20) {
@@ -351,7 +345,7 @@
          okText: '知道了'
        })
      } else {
        let key = md5(window.GLOB.appId + 'minke_software' + window.GLOB.appkey).toUpperCase().substr(-6)
        let key = md5(window.GLOB.appId + 'minke_software' + appkey).toUpperCase().substr(-6)
  
        let key1 = window.GLOB.licenseKey.substring(0, 6)
        let key2 = window.GLOB.licenseKey.substring(6, 14)
@@ -359,7 +353,7 @@
        let key4 = md5(key1 + key2).toUpperCase().substr(-6)
  
        if (key === key1 && key3 === key4) {
          let last = window.GLOB.appkey[window.GLOB.appkey.length - 1]
          let last = appkey[appkey.length - 1]
          let offset = 0
          let keys = {}
      
@@ -432,7 +426,7 @@
        func: _func,
        AppID: window.GLOB.appId,
        TimeStamp: timeStamp,
        appkey: window.GLOB.appkey,
        appkey: appkey,
        SessionUid: localStorage.getItem('SessionUid') || '',
        userid: _id,
        LoginUID: _id,
@@ -562,7 +556,8 @@
        let _param = {
          func: 's_Get_style',
          TypeCharOne: 'PC',
          LText: `select '${window.GLOB.appkey}'`,
          LText: `select '${appkey}'`,
          appkey: appkey
        }
        _param.userid = result.UserID
@@ -575,7 +570,7 @@
          
          if (res.status) {
            if (window.GLOB.sysType === 'local' && window.GLOB.systemType !== 'production') {
              if (md5(('mk' + window.GLOB.appkey + res.sys_datetime + res.member_type + res.registry_date).toLowerCase()) !== res.secret_key) {
              if (md5(('mk' + appkey + res.sys_datetime + res.member_type + res.registry_date).toLowerCase()) !== res.secret_key) {
                Modal.warning({
                  title: '密钥错误,请联系管理员!',
                  okText: '知道了'
@@ -619,7 +614,7 @@
            }
            if ([10, 20, 30, 40, 50, 60, 70, 80, 90, 100].includes(res.member_level)) {
              systemMsg.Member_Level = md5('mksoft' + window.GLOB.appkey + res.member_level)
              systemMsg.Member_Level = md5('mksoft' + appkey + res.member_level)
              if (!window.GLOB.memberLevel) {
                Object.defineProperty(window.GLOB, 'memberLevel', {
                  writable: false,
@@ -880,7 +875,6 @@
            lang={this.state.selectedlang}
            langList={this.state.langList}
            isDisabled={this.state.isDisabled}
            changelang={(value) => this.changelang(value)}
            handleSubmit={() => this.handleSubmit()}
            authLogin={this.authLogin}
            wrappedComponentRef={(inst) => this.loginformRef = inst}
src/views/login/loginform.jsx
@@ -18,7 +18,6 @@
class LoginTabForm extends Component {
  static propTpyes = {
    isDisabled: PropTypes.bool,
    changelang: PropTypes.func,
    handleSubmit: PropTypes.func,
    authLogin: PropTypes.func,
    dict: PropTypes.object,
@@ -174,8 +173,12 @@
    })
  }
  changelang = (item) => {
    this.props.changelang(item)
  changelang = (val) => {
    const _href = window.location.href.split('#')[0]
    localStorage.setItem(_href + 'lang', val)
    sessionStorage.setItem('lang', val)
    window.location.reload()
  }
  handleSubmit = e => {
src/views/menudesign/index.jsx
@@ -654,7 +654,7 @@
        
        if (item.action && item.action.length > 0) {
          item.action.forEach(btn => {
            if (btn.hidden === 'true') return
            if (btn.hidden === 'true' || btn.permission === 'false') return
            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
            _sort++
          })
@@ -669,13 +669,13 @@
          item.subcards.forEach(card => {
            card.elements && card.elements.forEach(cell => {
              if (cell.eleType !== 'button') return
              if (cell.hidden === 'true') return
              if (cell.hidden === 'true' || cell.permission === 'false') return
              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
              _sort++
            })
            card.backElements && card.backElements.forEach(cell => {
              if (cell.eleType !== 'button') return
              if (cell.hidden === 'true') return
              if (cell.hidden === 'true' || cell.permission === 'false') return
              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
              _sort++
            })
@@ -683,7 +683,7 @@
        } else if (item.type === 'balcony') {
          item.elements && item.elements.forEach(cell => {
            if (cell.eleType !== 'button') return
            if (cell.hidden === 'true') return
            if (cell.hidden === 'true' || cell.permission === 'false') return
            buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
            _sort++
          })
@@ -694,7 +694,7 @@
                loopCol(col.subcols)
              } else if (col.type === 'custom') {
                col.elements.forEach(cell => {
                  if (cell.eleType !== 'button' || cell.hidden === 'true') return
                  if (cell.eleType !== 'button' || cell.hidden === 'true' || cell.permission === 'false') return
                  buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
                  _sort++
                })
@@ -799,6 +799,11 @@
      let tbs = []
      let btns = this.getMenuMessage(config, tbs)
      if (config.permission === 'false') {
        btns = []
      }
      let arr = []
      tbs = tbs.filter(tb => {
        let _tb = tb.toLowerCase()
@@ -828,6 +833,7 @@
      }
      let interfaces = getFuncsAndInters(config)
      let urlFields = config.urlFields ? config.urlFields.join(',') : ''
      let param = {
        func: 'sPC_TrdMenu_AddUpt',
@@ -839,7 +845,7 @@
        EasyCode: config.easyCode || '',
        Template: 'CustomPage',
        MenuName: config.MenuName || '',
        PageParam: JSON.stringify({Template: 'CustomPage', OpenType: config.OpenType || 'newtab', hidden: config.hidden || 'false', menuColor: config.menuColor || '', interfaces}),
        PageParam: JSON.stringify({Template: 'CustomPage', OpenType: config.OpenType || 'newtab', hidden: config.hidden || 'false', menuColor: config.menuColor || '', interfaces, urlFields}),
        open_edition: config.open_edition,
        LText: '',
        LTexttb: '',
src/views/mkiframe/index.jsx
@@ -19,6 +19,8 @@
    type: 'CustomPage'
  }
  reloading = false
  UNSAFE_componentWillMount() {
    const { params, path } = this.props.match
    const { menuId, loginUid, bid } = params
@@ -112,6 +114,10 @@
    MKEmitter.addListener('modifyTabs', this.modifyTabs)
    MKEmitter.addListener('closeTabView', this.closeTabView)
    if (window.GLOB.forcedUpdate) {
      MKEmitter.addListener('reloadTabs', this.reloadTabs)
    }
    if (window.GLOB.sysType !== 'cloud') {
      Object.defineProperty(window, 'debugger', {
        configurable: true,
@@ -152,7 +158,24 @@
      return
    }
    MKEmitter.removeListener('modifyTabs', this.modifyTabs)
    MKEmitter.removeListener('reloadTabs', this.reloadTabs)
    MKEmitter.removeListener('closeTabView', this.closeTabView)
  }
  reloadTabs = () => {
    if (this.reloading) return
    this.reloading = true
    Api.getAppVersion(true).then(() => {
      window.location.reload()
    }, (message) => {
      notification.error({
        top: 92,
        message: message || '系统配置更新失败!',
        duration: 10
      })
    })
  }
  modifyTabs = (tab) => {
@@ -183,7 +206,10 @@
    let historys = sessionStorage.getItem('page_historys')
    historys = historys ? JSON.parse(historys) : []
    if (historys.length === 0) return
    if (historys.length === 0) {
      window.close()
      return
    }
    let tab = historys.shift()
src/views/mobdesign/index.jsx
@@ -1039,7 +1039,7 @@
          return
        } else if (item.type === 'card' || item.type === 'carousel' || item.type === 'timeline') {
          item.action && item.action.forEach(btn => {
            if (btn.hidden === 'true') return
            if (btn.hidden === 'true' || btn.permission === 'false') return
            m.children.push({
              key: btn.uuid,
@@ -1048,7 +1048,7 @@
          })
          item.subcards.forEach(card => {
            card.elements && card.elements.forEach(cell => {
              if (cell.eleType !== 'button' || cell.hidden === 'true') return
              if (cell.eleType !== 'button' || cell.hidden === 'true' || cell.permission === 'false') return
              m.children.push({
                key: cell.uuid,
@@ -1059,7 +1059,7 @@
            if (item.subtype !== 'dualdatacard') return
            
            card.backElements && card.backElements.forEach(cell => {
              if (cell.eleType !== 'button' || cell.hidden === 'true') return
              if (cell.eleType !== 'button' || cell.hidden === 'true' || cell.permission === 'false') return
              m.children.push({
                key: cell.uuid,
@@ -1069,7 +1069,7 @@
          })
        } else if (item.type === 'balcony') {
          item.elements && item.elements.forEach(cell => {
            if (cell.eleType !== 'button' || cell.hidden === 'true') return
            if (cell.eleType !== 'button' || cell.hidden === 'true' || cell.permission === 'false') return
            m.children.push({
              key: cell.uuid,
@@ -1098,7 +1098,7 @@
          }
        } else if (item.type === 'table') {
          item.action && item.action.forEach(btn => {
            if (btn.hidden === 'true') return
            if (btn.hidden === 'true' || btn.permission === 'false') return
            m.children.push({
              key: btn.uuid,
@@ -1111,7 +1111,7 @@
                loopCol(col.subcols)
              } else if (col.type === 'custom') {
                col.elements.forEach(cell => {
                  if (cell.eleType !== 'button' || cell.hidden === 'true') return
                  if (cell.eleType !== 'button' || cell.hidden === 'true' || cell.permission === 'false') return
                  m.children.push({
                    key: cell.uuid,
                    title: cell.label,
src/views/pcdesign/index.jsx
@@ -877,7 +877,7 @@
          return
        } else if (item.type === 'card' || item.type === 'carousel' || item.type === 'timeline') {
          item.action && item.action.forEach(btn => {
            if (btn.hidden === 'true') return
            if (btn.hidden === 'true' || btn.permission === 'false') return
            m.children.push({
              key: btn.uuid,
@@ -886,7 +886,7 @@
          })
          item.subcards.forEach(card => {
            card.elements && card.elements.forEach(cell => {
              if (cell.eleType !== 'button' || cell.hidden === 'true') return
              if (cell.eleType !== 'button' || cell.hidden === 'true' || cell.permission === 'false') return
              m.children.push({
                key: cell.uuid,
@@ -895,7 +895,7 @@
            })
            card.backElements && card.backElements.forEach(cell => {
              if (cell.eleType !== 'button' || cell.hidden === 'true') return
              if (cell.eleType !== 'button' || cell.hidden === 'true' || cell.permission === 'false') return
              m.children.push({
                key: cell.uuid,
@@ -905,7 +905,7 @@
          })
        } else if (item.type === 'balcony') {
          item.elements && item.elements.forEach(cell => {
            if (cell.eleType !== 'button' || cell.hidden === 'true') return
            if (cell.eleType !== 'button' || cell.hidden === 'true' || cell.permission === 'false') return
            m.children.push({
              key: cell.uuid,
@@ -923,7 +923,7 @@
          }
        } else if (item.type === 'table') {
          item.action.forEach(btn => {
            if (btn.hidden === 'true') return
            if (btn.hidden === 'true' || btn.permission === 'false') return
            m.children.push({
              key: btn.uuid,
@@ -936,7 +936,7 @@
                loopCol(col.subcols)
              } else if (col.type === 'custom') {
                col.elements.forEach(cell => {
                  if (cell.eleType !== 'button' || cell.hidden === 'true') return
                  if (cell.eleType !== 'button' || cell.hidden === 'true' || cell.permission === 'false') return
                  m.children.push({
                    key: cell.uuid,
                    title: cell.label,
src/views/rolemanage/header/index.jsx
@@ -7,14 +7,17 @@
class AppManageHeader extends Component {
  state = {
    avatar: sessionStorage.getItem('CloudAvatar') || avatar,
    userName: sessionStorage.getItem('CloudUserName')
    userName: sessionStorage.getItem('CloudUserName'),
    logo: sessionStorage.getItem('CloudLogo') || MainLogo
  }
  render () {
    const { app } = this.props
    const { logo } = this.state
    return (
      <header className="app-manage-header-container">
        <div className="header-logo"><img src={MainLogo} alt=""/></div>
        <div className="header-logo"><img src={logo} alt=""/></div>
        <div className="title">
          {`${app.remark} ${app.typename}`}
        </div>
src/views/systemfunc/header/index.jsx
@@ -8,14 +8,11 @@
  state = {
    userName: sessionStorage.getItem('CloudUserName'),
    avatar: sessionStorage.getItem('CloudAvatar') || avatar,
    logo: sessionStorage.getItem('CloudLogo') || MainLogo
  }
  exitManage = () => {
    window.close()
  }
  UNSAFE_componentWillMount () {
  }
  /**
@@ -28,9 +25,11 @@
  }
  render () {
    const { logo } = this.state
    return (
      <header className="sys-header-container ant-menu-dark">
        <div className="header-logo"><img src={MainLogo} alt=""/></div>
        <div className="header-logo"><img src={logo} alt=""/></div>
        <div className="title">HS</div>
        <div className="header-setting">
          <img src={this.state.avatar} alt=""/>
src/views/systemfunc/sidemenu/index.jsx
@@ -66,7 +66,7 @@
        if (menu) {
          menu = fromJS(menu).toJS()
          menu.param = {}
          menu.param.$searchkey = 'PrintTempNO'
          menu.param.$searchkey = 'printtempno'
          menu.param.$searchval = temp
          setTimeout(() => {
            MKEmitter.emit('modifyTabs', menu)
@@ -77,7 +77,7 @@
        if (menu) {
          menu = fromJS(menu).toJS()
          menu.param = {}
          menu.param.$searchkey = 'PrintTempNO'
          menu.param.$searchkey = 'printtempno'
          menu.param.$searchval = tabTemp
          setTimeout(() => {
            MKEmitter.emit('modifyTabs', menu)
src/views/tabledesign/index.jsx
@@ -459,7 +459,7 @@
          let _s = 1
          tab.components[0].action.forEach(btn => {
            if (btn.hidden === 'true') return
            if (btn.hidden === 'true' || btn.permission === 'false') return
            buttons.push(`select '${btn.uuid}' as menuid, '${btn.label}' as menuname, '${_s * 10}' as Sort, '${tab.components[0].uuid}' as parentid, 60 as Type`)
            _s++
@@ -471,7 +471,7 @@
                loopCol(col.subcols)
              } else if (col.type === 'custom') {
                col.elements.forEach(cell => {
                  if (cell.eleType !== 'button' || cell.hidden === 'true') return
                  if (cell.eleType !== 'button' || cell.hidden === 'true' || cell.permission === 'false') return
                  buttons.push(`select '${cell.uuid}' as menuid, '${cell.label}' as menuname, '${_s * 10}' as Sort, '${tab.components[0].uuid}' as parentid, 60 as Type`)
                  _s++
                })
@@ -486,7 +486,7 @@
          tbs.push(...item.$tables)
        }
        item.action.forEach(btn => {
          if (btn.hidden === 'true') return
          if (btn.hidden === 'true' || btn.permission === 'false') return
          buttons.push(`select '${btn.uuid}' as menuid, '${btn.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
          _sort++
@@ -498,7 +498,7 @@
              loopCol(col.subcols)
            } else if (col.type === 'custom') {
              col.elements.forEach(cell => {
                if (cell.eleType !== 'button' || cell.hidden === 'true') return
                if (cell.eleType !== 'button' || cell.hidden === 'true' || cell.permission === 'false') return
                buttons.push(`select '${cell.uuid}' as menuid,  '${cell.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
                _sort++
              })
@@ -581,6 +581,7 @@
      }
      let interfaces = getFuncsAndInters(config)
      let urlFields = config.urlFields ? config.urlFields.join(',') : ''
      let param = {
        func: 'sPC_TrdMenu_AddUpt',
@@ -592,7 +593,7 @@
        EasyCode: config.easyCode || '',
        Template: 'BaseTable',
        MenuName: config.MenuName || '',
        PageParam: JSON.stringify({Template: 'BaseTable', OpenType: config.OpenType || 'newtab', hidden: config.hidden || 'false', menuColor: config.menuColor || '', interfaces}),
        PageParam: JSON.stringify({Template: 'BaseTable', OpenType: config.OpenType || 'newtab', hidden: config.hidden || 'false', menuColor: config.menuColor || '', interfaces, urlFields}),
        open_edition: config.open_edition,
        LText: '',
        LTexttb: '',
@@ -892,6 +893,13 @@
              <div className={'menu-view' + (menuloading ? ' saving' : '')}>
                <Card bordered={false} extra={
                  <div className="mk-opeartion-list">
                    {/* <Dropdown overlay={
                      <div className="mk-button-dropdown-wrap">
                        <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
                      </div>
                    } trigger={['click']} placement="bottomCenter">
                      <Button className="mk-button-more">更多<DownOutlined/></Button>
                    </Dropdown> */}
                    {config ? <Debug config={config}/> : null}
                    {config ? <Transfer config={config}/> : null}
                    {config ? <Unattended config={config} updateConfig={this.updateConfig}/> : null}