From 8a717c6b8f54a45ff013b8e68599aad2918cc88c Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期四, 23 二月 2023 17:35:49 +0800
Subject: [PATCH] Merge branch 'develop'

---
 src/templates/zshare/modalform/index.jsx                                                  |   14 
 src/tabviews/custom/components/module/voucher/resetRemark/index.jsx                       |   62 
 src/menu/components/share/markcomponent/index.jsx                                         |    3 
 src/menu/components/card/cardcellcomponent/elementform/index.jsx                          |   98 
 src/assets/img/picture-fill.png                                                           |    0 
 src/menu/components/share/actioncomponent/dragaction/index.jsx                            |    3 
 src/tabviews/custom/popview/index.jsx                                                     |    7 
 src/templates/modalconfig/index.jsx                                                       |   29 
 src/tabviews/custom/components/module/voucher/resetAttach/documents/index.scss            |    0 
 src/mob/modalconfig/index.jsx                                                             |   34 
 src/tabviews/commontable/index.jsx                                                        |    1 
 src/assets/img/rar.png                                                                    |    0 
 src/menu/datasource/verifycard/index.jsx                                                  |  153 +
 src/assets/css/main.scss                                                                  |    6 
 src/tabviews/custom/components/card/cardcellList/index.jsx                                |    4 
 src/tabviews/custom/components/card/prop-card/index.jsx                                   |   24 
 src/tabviews/custom/components/module/voucher/resetAttach/index.scss                      |  235 +
 src/menu/components/card/cardcellcomponent/index.jsx                                      |   26 
 src/menu/components/form/tab-form/index.jsx                                               |   28 
 src/tabviews/custom/components/module/voucher/resetAttach/documents/index.jsx             |  120 +
 src/pc/menushell/card.jsx                                                                 |    3 
 src/tabviews/custom/components/table/normal-table/index.jsx                               |   15 
 src/tabviews/custom/components/timeline/normal-timeline/index.jsx                         |   14 
 src/menu/components/card/cardcomponent/index.jsx                                          |   19 
 src/menu/components/group/groupcomponents/card.jsx                                        |    3 
 src/tabviews/zshare/mutilform/index.jsx                                                   |   13 
 src/menu/components/share/actioncomponent/actionform/index.jsx                            |    4 
 src/tabviews/custom/index.jsx                                                             |    7 
 src/menu/components/card/cardcomponent/index.scss                                         |   31 
 src/menu/components/tabs/tabcomponents/card.jsx                                           |    3 
 src/tabviews/custom/components/module/voucher/saveAsTemp/index.jsx                        |  122 +
 src/templates/zshare/modalform/modaleditable/index.jsx                                    |    8 
 package-lock.json                                                                         |  115 
 src/menu/datasource/verifycard/index.scss                                                 |   17 
 src/menu/components/share/actioncomponent/dragaction/card.jsx                             |    2 
 src/tabviews/zshare/topSearch/index.jsx                                                   |   10 
 src/menu/components/card/doublecardcomponent/index.scss                                   |  118 
 src/menu/components/form/simple-form/index.jsx                                            |   28 
 src/tabviews/custom/components/table/edit-table/index.jsx                                 |   15 
 src/views/tabledesign/index.jsx                                                           |   14 
 src/tabviews/custom/components/module/voucher/saveAsTemp/index.scss                       |    0 
 src/tabviews/subtable/index.jsx                                                           |    1 
 src/mob/mobshell/card.jsx                                                                 |    3 
 src/assets/img/file-ppt-fill.png                                                          |    0 
 src/tabviews/custom/components/card/prop-card/index.scss                                  |    5 
 src/tabviews/custom/components/card/table-card/index.jsx                                  |   15 
 src/menu/components/module/voucher/options.jsx                                            |   21 
 src/menu/components/form/step-form/index.jsx                                              |   23 
 src/templates/modalconfig/settingform/index.jsx                                           |   24 
 src/menu/datasource/verifycard/settingform/index.jsx                                      |   50 
 src/mob/components/tabs/tabcomponents/card.jsx                                            |    3 
 src/templates/sharecomponent/searchcomponent/dragsearch/index.jsx                         |    8 
 src/tabviews/custom/components/module/voucher/voucherTable/index.jsx                      |   16 
 src/tabviews/custom/components/module/voucher/resetRemark/index.scss                      |    0 
 src/assets/img/file-excel-fill.png                                                        |    0 
 src/templates/zshare/modalform/modaleditable/index.scss                                   |    4 
 src/utils/utils-datamanage.js                                                             |   17 
 src/pc/modulesource/option.jsx                                                            |    5 
 src/tabviews/custom/components/code/sand-box/index.jsx                                    |   16 
 src/tabviews/custom/components/card/cardItem/index.scss                                   |    6 
 src/tabviews/custom/components/tree/antd-tree/index.jsx                                   |  107 
 src/mob/modulesource/option.jsx                                                           |    5 
 src/templates/comtableconfig/updatetable/index.jsx                                        |   14 
 src/menu/datasource/index.jsx                                                             |    4 
 src/menu/components/module/voucher/index.jsx                                              |    4 
 src/menu/components/tree/antd-tree/index.jsx                                              |   64 
 src/views/billprint/index.jsx                                                             |    7 
 src/menu/menushell/card.jsx                                                               |    3 
 src/tabviews/subtabtable/index.jsx                                                        |    1 
 src/tabviews/custom/components/module/voucher/printVoucher/index.jsx                      |  354 ++
 src/tabviews/zshare/actionList/normalbutton/index.jsx                                     |   54 
 src/views/mobdesign/index.jsx                                                             |   11 
 src/menu/modulesource/option.jsx                                                          |    5 
 src/tabviews/custom/components/card/data-card/index.jsx                                   |   30 
 src/assets/img/file-word-fill.png                                                         |    0 
 src/menu/components/card/double-data-card/options.jsx                                     |  240 ++
 src/tabviews/custom/components/module/voucher/index.jsx                                   |  588 ++++
 src/menu/components/share/actioncomponent/formconfig.jsx                                  |    2 
 src/tabviews/custom/components/group/normal-group/index.jsx                               |    7 
 src/menu/components/card/double-data-card/index.jsx                                       |  658 +++++
 src/menu/components/card/doublecardcomponent/index.jsx                                    |  318 ++
 src/tabviews/custom/components/carousel/prop-card/index.jsx                               |   12 
 src/tabviews/custom/components/module/voucher/resetAttach/addAttach/index.jsx             |  160 +
 src/menu/components/search/main-search/options.jsx                                        |   11 
 src/menu/components/card/cardcomponent/options.jsx                                        |    5 
 src/tabviews/custom/components/module/voucher/printVoucher/index.scss                     |  126 +
 src/tabviews/custom/components/table/base-table/index.jsx                                 |    6 
 src/menu/components/card/doublecardcomponent/options.jsx                                  |  194 +
 src/tabviews/custom/components/card/double-data-card/index.scss                           |  327 ++
 src/menu/components/tree/antd-tree/index.scss                                             |    3 
 src/assets/img/file-fill.png                                                              |    0 
 src/tabviews/custom/components/card/double-data-card/index.jsx                            |  956 +++++++
 src/templates/formtabconfig/index.jsx                                                     |   13 
 src/tabviews/custom/components/card/cardItem/index.jsx                                    |   10 
 src/components/qrcode/index.jsx                                                           |    4 
 src/tabviews/custom/components/carousel/data-card/index.jsx                               |   13 
 src/tabviews/custom/components/tree/antd-tree/index.scss                                  |   34 
 src/assets/img/file-pdf-fill.png                                                          |    0 
 src/tabviews/custom/components/card/data-card/index.scss                                  |    9 
 src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.jsx  |  266 ++
 src/tabviews/custom/components/module/voucher/loadFromTemp/index.scss                     |  117 
 src/menu/components/search/main-search/dragsearch/index.jsx                               |    8 
 src/menu/components/share/pastecomponent/index.jsx                                        |    2 
 src/tabviews/custom/components/module/voucher/loadFromTemp/index.jsx                      |  103 
 src/tabviews/custom/components/share/tabtransfer/index.jsx                                |    7 
 src/templates/zshare/formconfig.jsx                                                       |   35 
 src/templates/zshare/verifycard/index.jsx                                                 |    2 
 src/tabviews/custom/components/module/voucher/resetAttach/index.jsx                       |  297 ++
 src/tabviews/zshare/actionList/printbutton/index.jsx                                      |   53 
 src/menu/components/card/cardcellcomponent/formconfig.jsx                                 |    2 
 src/templates/sharecomponent/searchcomponent/settingform/index.jsx                        |   12 
 src/tabviews/custom/components/module/voucher/index.scss                                  |   51 
 src/menu/modalconfig/index.jsx                                                            |   32 
 src/menu/components/module/voucher/index.scss                                             |    2 
 package.json                                                                              |    1 
 src/menu/components/table/base-table/index.jsx                                            |    9 
 src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.scss |   29 
 src/menu/components/card/double-data-card/index.scss                                      |  148 +
 118 files changed, 6,669 insertions(+), 491 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index a469c79..f445383 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3158,6 +3158,12 @@
       "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz",
       "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw=="
     },
+    "@types/raf": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.0.tgz",
+      "integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==",
+      "optional": true
+    },
     "@types/react": {
       "version": "16.9.2",
       "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.2.tgz",
@@ -5216,6 +5222,11 @@
         "node-int64": "^0.4.0"
       }
     },
+    "btoa": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
+      "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="
+    },
     "buffer": {
       "version": "4.9.1",
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
@@ -5407,6 +5418,45 @@
       "version": "1.0.30001399",
       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001399.tgz",
       "integrity": "sha512-4vQ90tMKS+FkvuVWS5/QY1+d805ODxZiKFzsU8o/RsVJz49ZSRR8EjykLJbqhzdPgadbX6wB538wOzle3JniRA=="
+    },
+    "canvg": {
+      "version": "3.0.10",
+      "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz",
+      "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==",
+      "optional": true,
+      "requires": {
+        "@babel/runtime": "^7.12.5",
+        "@types/raf": "^3.4.0",
+        "core-js": "^3.8.3",
+        "raf": "^3.4.1",
+        "regenerator-runtime": "^0.13.7",
+        "rgbcolor": "^1.0.1",
+        "stackblur-canvas": "^2.0.0",
+        "svg-pathdata": "^6.0.3"
+      },
+      "dependencies": {
+        "@babel/runtime": {
+          "version": "7.20.13",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
+          "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==",
+          "optional": true,
+          "requires": {
+            "regenerator-runtime": "^0.13.11"
+          }
+        },
+        "core-js": {
+          "version": "3.28.0",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.28.0.tgz",
+          "integrity": "sha512-GiZn9D4Z/rSYvTeg1ljAIsEqFm0LaN9gVtwDCrKL80zHtS31p9BAjmTxVqTQDMpwlMolJZOFntUG2uwyj7DAqw==",
+          "optional": true
+        },
+        "regenerator-runtime": {
+          "version": "0.13.11",
+          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+          "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+          "optional": true
+        }
+      }
     },
     "capture-exit": {
       "version": "2.0.0",
@@ -7025,6 +7075,12 @@
       "requires": {
         "domelementtype": "1"
       }
+    },
+    "dompurify": {
+      "version": "2.4.4",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.4.tgz",
+      "integrity": "sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ==",
+      "optional": true
     },
     "domutils": {
       "version": "1.7.0",
@@ -9504,6 +9560,11 @@
       "version": "4.2.1",
       "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz",
       "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q=="
+    },
+    "fflate": {
+      "version": "0.4.8",
+      "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
+      "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="
     },
     "figgy-pudding": {
       "version": "3.5.1",
@@ -12699,6 +12760,42 @@
       "version": "0.0.0",
       "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
       "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
+    },
+    "jspdf": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz",
+      "integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==",
+      "requires": {
+        "@babel/runtime": "^7.14.0",
+        "atob": "^2.1.2",
+        "btoa": "^1.2.1",
+        "canvg": "^3.0.6",
+        "core-js": "^3.6.0",
+        "dompurify": "^2.2.0",
+        "fflate": "^0.4.8",
+        "html2canvas": "^1.0.0-rc.5"
+      },
+      "dependencies": {
+        "@babel/runtime": {
+          "version": "7.20.13",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
+          "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==",
+          "requires": {
+            "regenerator-runtime": "^0.13.11"
+          }
+        },
+        "core-js": {
+          "version": "3.28.0",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.28.0.tgz",
+          "integrity": "sha512-GiZn9D4Z/rSYvTeg1ljAIsEqFm0LaN9gVtwDCrKL80zHtS31p9BAjmTxVqTQDMpwlMolJZOFntUG2uwyj7DAqw==",
+          "optional": true
+        },
+        "regenerator-runtime": {
+          "version": "0.13.11",
+          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+          "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+        }
+      }
     },
     "jsprim": {
       "version": "1.4.1",
@@ -18887,6 +18984,12 @@
       "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz",
       "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM="
     },
+    "rgbcolor": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
+      "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
+      "optional": true
+    },
     "rimraf": {
       "version": "2.7.1",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -19982,6 +20085,12 @@
       "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz",
       "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA=="
     },
+    "stackblur-canvas": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz",
+      "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==",
+      "optional": true
+    },
     "stacktrace-parser": {
       "version": "0.1.10",
       "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz",
@@ -20260,6 +20369,12 @@
       "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.2.tgz",
       "integrity": "sha512-1gtApepKFweigFZj3sGO8KT8LvVZK8io146EzXrpVuWCDAbISz/yMucco3hWTkpZNoPabM+dnMOpy6Swue68Zg=="
     },
+    "svg-pathdata": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
+      "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
+      "optional": true
+    },
     "svgo": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.0.tgz",
diff --git a/package.json b/package.json
index 5ae1493..2f95956 100644
--- a/package.json
+++ b/package.json
@@ -61,6 +61,7 @@
     "jest-watch-typeahead": "0.3.1",
     "js-table2excel": "^1.0.3",
     "jsbarcode": "^3.11.3",
+    "jspdf": "^2.5.1",
     "jssha": "^3.2.0",
     "jszip": "^3.10.0",
     "md5": "^2.2.1",
diff --git a/src/assets/css/main.scss b/src/assets/css/main.scss
index e5ada0e..6f5c18a 100644
--- a/src/assets/css/main.scss
+++ b/src/assets/css/main.scss
@@ -326,6 +326,12 @@
     background: rgba(0, 0, 0, 0);
   }
 }
+.moveable-modal {
+  overflow-x: hidden!important;
+  .ant-modal-header {
+    cursor: move;
+  }
+}
 
 .ant-drawer {
   z-index: 1080!important;
diff --git a/src/assets/img/file-excel-fill.png b/src/assets/img/file-excel-fill.png
new file mode 100644
index 0000000..3f7f242
--- /dev/null
+++ b/src/assets/img/file-excel-fill.png
Binary files differ
diff --git a/src/assets/img/file-fill.png b/src/assets/img/file-fill.png
new file mode 100644
index 0000000..113b328
--- /dev/null
+++ b/src/assets/img/file-fill.png
Binary files differ
diff --git a/src/assets/img/file-pdf-fill.png b/src/assets/img/file-pdf-fill.png
new file mode 100644
index 0000000..054d2ca
--- /dev/null
+++ b/src/assets/img/file-pdf-fill.png
Binary files differ
diff --git a/src/assets/img/file-ppt-fill.png b/src/assets/img/file-ppt-fill.png
new file mode 100644
index 0000000..6b784ed
--- /dev/null
+++ b/src/assets/img/file-ppt-fill.png
Binary files differ
diff --git a/src/assets/img/file-word-fill.png b/src/assets/img/file-word-fill.png
new file mode 100644
index 0000000..37d9c52
--- /dev/null
+++ b/src/assets/img/file-word-fill.png
Binary files differ
diff --git a/src/assets/img/picture-fill.png b/src/assets/img/picture-fill.png
new file mode 100644
index 0000000..782d46b
--- /dev/null
+++ b/src/assets/img/picture-fill.png
Binary files differ
diff --git a/src/assets/img/rar.png b/src/assets/img/rar.png
new file mode 100644
index 0000000..830f5db
--- /dev/null
+++ b/src/assets/img/rar.png
Binary files differ
diff --git a/src/components/qrcode/index.jsx b/src/components/qrcode/index.jsx
index 0fbc0cc..7fdc4fa 100644
--- a/src/components/qrcode/index.jsx
+++ b/src/components/qrcode/index.jsx
@@ -5,7 +5,7 @@
 
 import './index.scss'
 
-class BarCode extends Component {
+class MkQrCode extends Component {
   static propTpyes = {
     card: PropTypes.object,  // 鏉$爜璁剧疆
     value: PropTypes.any,    // 鏉$爜鍊�
@@ -55,4 +55,4 @@
   }
 }
 
-export default BarCode
\ No newline at end of file
+export default MkQrCode
\ No newline at end of file
diff --git a/src/menu/components/card/cardcellcomponent/elementform/index.jsx b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
index d2a553f..009826c 100644
--- a/src/menu/components/card/cardcellcomponent/elementform/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -48,7 +48,7 @@
   }
 
   UNSAFE_componentWillMount () {
-    const { card, config } = this.props
+    const { card, config, side } = this.props
     let _options = this.getOptions(card.eleType, card.datatype, card.link, (card.showType || 'line'), card.showInfo, card.fixStyle || '', card.posterType || '')
     
     this.setState({
@@ -64,20 +64,38 @@
 
         if (item.key === 'field' || item.key === 'linkurl' || item.key === 'bgImage' || item.key === 'posterField') {
           item.options = []
-          config.columns.forEach(col => {
-            let label = `${col.field}锛�${col.label}锛塦
-            if (/^(Int|Decimal)/ig.test(col.datatype) && (card.eleType === 'number' || card.eleType === 'slider')) {
-              item.options.push({
-                value: col.field,
-                text: label
-              })
-            } else if (/^(Nvarchar|date)/ig.test(col.datatype) && card.eleType !== 'number' && card.eleType !== 'slider') {
-              item.options.push({
-                value: col.field,
-                text: label
-              })
-            }
-          })
+          
+          if (side === 'sub') {
+            config.subColumns.forEach(col => {
+              let label = `${col.field}锛�${col.label}锛塦
+              if (/^(Int|Decimal)/ig.test(col.datatype) && (card.eleType === 'number' || card.eleType === 'slider')) {
+                item.options.push({
+                  value: col.field,
+                  text: label
+                })
+              } else if (/^(Nvarchar|date)/ig.test(col.datatype) && card.eleType !== 'number' && card.eleType !== 'slider') {
+                item.options.push({
+                  value: col.field,
+                  text: label
+                })
+              }
+            })
+          } else {
+            config.columns.forEach(col => {
+              let label = `${col.field}锛�${col.label}锛塦
+              if (/^(Int|Decimal)/ig.test(col.datatype) && (card.eleType === 'number' || card.eleType === 'slider')) {
+                item.options.push({
+                  value: col.field,
+                  text: label
+                })
+              } else if (/^(Nvarchar|date)/ig.test(col.datatype) && card.eleType !== 'number' && card.eleType !== 'slider') {
+                item.options.push({
+                  value: col.field,
+                  text: label
+                })
+              }
+            })
+          }
         } else if (item.key === 'value' && card.eleType === 'slider') {
           item.type = 'number'
           item.label = '鍊�'
@@ -140,7 +158,7 @@
       }
     } else if (eleType === 'icon') {
       if (datatype === 'dynamic') {
-        _options.push('field')
+        _options.push('field', 'noValue')
       } else {
         _options.push('icon')
       }
@@ -159,7 +177,7 @@
    * 3銆佸垏鎹㈡爣绛剧被鍨嬶紝閲嶇疆鍙�夋爣绛�
    */
   selectChange = (key, value, option) => {
-    const { card, config } = this.props
+    const { card, config, side } = this.props
     const { datatype, eleType, showType, showInfo, fixStyle, posterType } = this.state
 
     if (key === 'eleType') {
@@ -170,21 +188,39 @@
 
         if (item.key === 'field') {
           item.options = []
-          config.columns.forEach(col => {
-            let label = `${col.field}锛�${col.label}锛塦
 
-            if (/^(Int|Decimal)/ig.test(col.datatype) && (value === 'number' || value === 'slider')) {
-              item.options.push({
-                value: col.field,
-                text: label
-              })
-            } else if (/^(Nvarchar|date)/ig.test(col.datatype) && value !== 'number' && value !== 'slider') {
-              item.options.push({
-                value: col.field,
-                text: label
-              })
-            }
-          })
+          if (side === 'sub') {
+            config.subColumns.forEach(col => {
+              let label = `${col.field}锛�${col.label}锛塦
+              if (/^(Int|Decimal)/ig.test(col.datatype) && (value === 'number' || value === 'slider')) {
+                item.options.push({
+                  value: col.field,
+                  text: label
+                })
+              } else if (/^(Nvarchar|date)/ig.test(col.datatype) && value !== 'number' && value !== 'slider') {
+                item.options.push({
+                  value: col.field,
+                  text: label
+                })
+              }
+            })
+          } else {
+            config.columns.forEach(col => {
+              let label = `${col.field}锛�${col.label}锛塦
+  
+              if (/^(Int|Decimal)/ig.test(col.datatype) && (value === 'number' || value === 'slider')) {
+                item.options.push({
+                  value: col.field,
+                  text: label
+                })
+              } else if (/^(Nvarchar|date)/ig.test(col.datatype) && value !== 'number' && value !== 'slider') {
+                item.options.push({
+                  value: col.field,
+                  text: label
+                })
+              }
+            })
+          }
         } else if (item.key === 'value') {
           if (value === 'slider') {
             item.type = 'number'
diff --git a/src/menu/components/card/cardcellcomponent/formconfig.jsx b/src/menu/components/card/cardcellcomponent/formconfig.jsx
index 7d87970..859dc58 100644
--- a/src/menu/components/card/cardcellcomponent/formconfig.jsx
+++ b/src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -28,7 +28,7 @@
     anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, cards.uuid) || []
   }
 
-  if (cards.type === 'table' || (cards.type === 'card' && cards.subtype === 'datacard')) {
+  if (cards.type === 'table' || (cards.type === 'card' && cards.subtype !== 'propcard')) {
     _options.push({value: 'sequence', text: '搴忓彿'})
   } else if (card.eleType === 'sequence') { // 鎷栨嫿娣诲姞绫诲瀷杞崲
     card.eleType = 'text'
diff --git a/src/menu/components/card/cardcellcomponent/index.jsx b/src/menu/components/card/cardcellcomponent/index.jsx
index 454f5a9..9459118 100644
--- a/src/menu/components/card/cardcellcomponent/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/index.jsx
@@ -122,11 +122,12 @@
     this.setState({elements: this.state.elements.filter(item => item.uuid !== eleId)})
   }
 
-  cardAddElement = (id, element) => {
-    const { cardCell } = this.props
+  cardAddElement = (id, element, type) => {
+    const { cardCell, side } = this.props
 
     if (id !== cardCell.uuid) return
     if (window.GLOB.$lock) return
+    if (type && side !== type) return
 
     window.GLOB.$lock = true
     setTimeout(() => {
@@ -535,9 +536,13 @@
   }
 
   handleSubConfig = (item) => {
-    const { cards } = this.props
+    const { cards, side } = this.props
     const { appType } = this.state
     let btn = fromJS(item).toJS()
+
+    if (side === 'sub') {
+      btn.$sub = true
+    }
 
     if ((sessionStorage.getItem('style-control') && sessionStorage.getItem('style-control') === 'true')) return
 
@@ -693,7 +698,7 @@
   }
 
   getVerify = (card) => {
-    const { cards } = this.props
+    const { cards, side } = this.props
 
     if (!card) return null
 
@@ -701,13 +706,13 @@
       return <VerifyCard
         card={card}
         config={cards}
-        columns={cards.columns}
+        columns={side === 'sub' ? cards.subColumns : cards.columns}
         wrappedComponentRef={(inst) => this.verifyRef = inst}
       />
     } else if (card.OpenType === 'excelIn') {
       return <VerifyExcelIn
         card={card}
-        columns={cards.columns}
+        columns={side === 'sub' ? cards.subColumns : cards.columns}
         wrappedComponentRef={(inst) => this.verifyRef = inst}
       />
     } else if (card.OpenType === 'excelOut') {
@@ -719,14 +724,14 @@
     } else if (card.OpenType === 'funcbutton' && card.funcType === 'print') {
       return <VerifyPrint
         card={card}
-        columns={cards.columns}
+        columns={side === 'sub' ? cards.subColumns : cards.columns}
         wrappedComponentRef={(inst) => this.verifyRef = inst}
       />
     }
   }
 
   render() {
-    const { cards, cardCell } = this.props
+    const { cards, cardCell, side } = this.props
     const { elements, visible, actvisible, profVisible, card, record } = this.state
 
     return (
@@ -734,7 +739,7 @@
         <DragElement
           list={elements}
           parent={cardCell}
-          fields={cards.columns}
+          fields={side === 'sub' ? cards.subColumns : cards.columns}
           updateMarks={this.updateMarks}
           handleList={this.handleList}
           handleMenu={this.handleElement}
@@ -757,6 +762,7 @@
           >
             <ElementForm
               card={card}
+              side={side}
               formlist={this.state.formlist}
               inputSubmit={this.handleSubmit}
               config={cards}
@@ -771,7 +777,7 @@
             maskClosable={false}
             onCancel={this.editModalCancel}
             footer={[
-              record && record.intertype === 'inner' ? <CreateFunc key="create" ref="btnCreatFunc" trigger={this.creatFunc}/> : null,
+              record && record.intertype === 'inner' && cards.subtype === 'dualdatacard' ? <CreateFunc key="create" ref="btnCreatFunc" trigger={this.creatFunc}/> : null,
               <Button key="cancel" onClick={this.editModalCancel}>鍙栨秷</Button>,
               <Button key="confirm" type="primary" onClick={this.handleActionSubmit}>纭畾</Button>
             ]}
diff --git a/src/menu/components/card/cardcomponent/index.jsx b/src/menu/components/card/cardcomponent/index.jsx
index 6bd283e..a7efb6a 100644
--- a/src/menu/components/card/cardcomponent/index.jsx
+++ b/src/menu/components/card/cardcomponent/index.jsx
@@ -13,7 +13,7 @@
 import './index.scss'
 
 const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
-const CardCellComponent = asyncComponent(() => import('../cardcellcomponent'))
+const CardCellComponent = asyncComponent(() => import('@/menu/components/card/cardcellcomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const PasteController = asyncIconComponent(() => import('@/components/paste'))
 
@@ -311,9 +311,24 @@
     }
     _style = resetStyle(_style)
 
+    let checkAll = ''
+    if ((cards.subtype === 'datacard' || cards.subtype === 'dualdatacard') && card.$cardType === 'extendCard') {
+      checkAll = card.setting.checkAll === 'show' ? ' mk-checkable mk-extend-card' : ''
+      if (checkAll && cards.wrap.selStyle === 'check square') {
+        checkAll = ' mk-checkable square mk-extend-card'
+      }
+    } else if (cards.subtype === 'datacard') {
+      if (cards.wrap.selStyle === 'check') {
+        checkAll = ' mk-checkable'
+      } else if (cards.wrap.selStyle === 'check square') {
+        checkAll = ' mk-checkable square'
+      }
+    }
+
     return (
       <Col span={card.setting.width || 6}>
-        <div className={'card-item ' + (card.setting.btnControl || '')} style={_style} onDoubleClick={(e) => {e.stopPropagation(); this.doubleClickCard()}} id={card.uuid}>
+        <div className={'card-item ' + (card.setting.btnControl || '') + checkAll} 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}/>
           <div className="card-control" onDoubleClick={(e) => e.stopPropagation()}>
             <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
diff --git a/src/menu/components/card/cardcomponent/index.scss b/src/menu/components/card/cardcomponent/index.scss
index a32dfaa..62291ee 100644
--- a/src/menu/components/card/cardcomponent/index.scss
+++ b/src/menu/components/card/cardcomponent/index.scss
@@ -62,3 +62,34 @@
     background: rgba(255, 255, 255, 0.55);
   }
 }
+.card-item {
+  .circle-select {
+    position: relative;
+    display: none;
+    width: 16px;
+    height: 16px;
+    border: 1px solid #cccccc;
+    border-radius: 50%;
+    box-sizing: content-box;
+    margin: auto;
+    margin-right: 5px;
+    background-color: #ffffff;
+    transition: border-color 0.2s;
+    cursor: pointer;
+  }
+}
+// square
+.card-item.mk-checkable {
+  display: flex;
+  .circle-select {
+    display: inline-block;
+  }
+  .model-menu-card-cell-list {
+    flex: 1;
+  }
+}
+.card-item.mk-checkable.square {
+  .circle-select {
+    border-radius: 0;
+  }
+}
diff --git a/src/menu/components/card/cardcomponent/options.jsx b/src/menu/components/card/cardcomponent/options.jsx
index dbcca49..02d46f2 100644
--- a/src/menu/components/card/cardcomponent/options.jsx
+++ b/src/menu/components/card/cardcomponent/options.jsx
@@ -73,7 +73,7 @@
       controlFields: [
         {field: 'transform', values: ['multi']},
       ],
-      forbid: appType === 'mob'
+      forbid: appType === 'mob' || subtype === 'dualdatacard'
     },
     {
       type: 'select',
@@ -91,7 +91,8 @@
         {value: 'opacity', label: '閫忔槑搴�'},
         {value: 'rotateX', label: '绾靛悜灞曞紑'},
         {value: 'rotateY', label: '妯悜灞曞紑'},
-      ]
+      ],
+      forbid: appType === 'mob' || subtype === 'dualdatacard'
     },
     {
       type: 'text',
diff --git a/src/menu/components/card/double-data-card/index.jsx b/src/menu/components/card/double-data-card/index.jsx
new file mode 100644
index 0000000..32dd303
--- /dev/null
+++ b/src/menu/components/card/double-data-card/index.jsx
@@ -0,0 +1,658 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Popover, Modal, Pagination, message } from 'antd'
+import { PlusOutlined, PlusCircleOutlined, PlusSquareOutlined, EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, DownOutlined } from '@ant-design/icons'
+
+import asyncComponent from '@/utils/asyncComponent'
+import asyncIconComponent from '@/utils/asyncIconComponent'
+import { resetStyle, getTables } from '@/utils/utils-custom.js'
+import MKEmitter from '@/utils/events.js'
+import Utils from '@/utils/utils.js'
+import getWrapForm from './options'
+import './index.scss'
+
+const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const CardComponent = asyncComponent(() => import('../cardcomponent'))
+const DoubleCardComponent = asyncComponent(() => import('../doublecardcomponent'))
+const MobPagination = asyncIconComponent(() => import('@/menu/components/share/mobPagination'))
+const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
+const PasteComponent = asyncIconComponent(() => import('@/components/paste'))
+const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
+const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
+const ActionComponent = asyncComponent(() => import('@/menu/components/share/actioncomponent'))
+
+const { confirm } = Modal
+
+class DoubleDataCardEditComponent extends Component {
+  static propTpyes = {
+    card: PropTypes.object,
+    deletecomponent: PropTypes.func,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    card: null,
+    appType: sessionStorage.getItem('appType'),
+    back: false
+  }
+
+  UNSAFE_componentWillMount () {
+    const { card } = this.props
+
+    if (card.isNew) {
+      let _card = {
+        uuid: card.uuid,
+        type: card.type,
+        format: 'array',   // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
+        pageable: true,    // 缁勪欢灞炴�� - 鏄惁鍙垎椤�
+        switchable: true,  // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹�
+        width: card.width || 24,
+        name: card.name,
+        subtype: card.subtype,
+        setting: { interType: 'system' },
+        wrap: { name: card.name, width: card.width || 24, title: '', pagestyle: 'page', cardType: '' },
+        style: { marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px' },
+        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
+        columns: [],
+        subColumns: [],
+        scripts: [],
+        action: [],
+        search: [],
+        subcards: [{
+          uuid: Utils.getuuid(),
+          setting: { width: 24, display: 'default', position: 'bottom' },
+          style: {
+            borderWidth: '1px', borderColor: '#e8e8e8',
+            paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
+            marginLeft: '8px', marginRight: '8px', marginTop: '8px'
+          },
+          elements: [{
+            uuid: Utils.getuuid(),
+            eleType: 'text',
+            datatype: 'static',
+            width: 12,
+            value: '寰幆鍖哄煙'
+          }],
+          backSetting: {width: 24},
+          backStyle: {
+            borderLeftWidth: '1px', borderLeftColor: '#e8e8e8',
+            borderRightWidth: '1px', borderRightColor: '#e8e8e8',
+            borderBottomWidth: '1px', borderBottomColor: '#e8e8e8',
+            paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
+            marginLeft: '8px', marginRight: '8px'
+          },
+          backElements: [{
+            uuid: Utils.getuuid(),
+            eleType: 'text',
+            datatype: 'static',
+            width: 12,
+            value: '瀛愯〃鍖哄煙'
+          }]
+        }]
+      }
+
+      if (card.config) {
+        let config = fromJS(card.config).toJS()
+
+        _card.wrap = config.wrap
+        _card.wrap.name = card.name
+        _card.style = config.style
+        _card.headerStyle = config.headerStyle
+
+        _card.setting = config.setting
+        _card.columns = config.columns
+        _card.scripts = config.scripts
+
+        _card.subcards = config.subcards.map(scard => {
+          scard.uuid = Utils.getuuid()
+          scard.elements = scard.elements.map(elem => {
+            elem.uuid = Utils.getuuid()
+            return elem
+          })
+          scard.backElements = scard.backElements.map(elem => {
+            elem.uuid = Utils.getuuid()
+            return elem
+          })
+          return scard
+        })
+        _card.action = config.action.map(col => {
+          col.uuid = Utils.getuuid()
+          return col
+        })
+        _card.search = config.search.map(col => {
+          col.uuid = Utils.getuuid()
+          return col
+        })
+      }
+
+      this.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.errors = []
+    let columns = card.columns.map(c => c.field)
+
+    if (card.setting.interType === 'system' && card.setting.execute !== 'false' && !card.setting.dataresource) {
+      card.errors.push({ level: 0, detail: '鏈缃暟鎹簮锛�'})
+    } else if (card.setting.interType === 'system' && card.setting.execute === 'false' && card.scripts.filter(script => script.status !== 'false').length === 0) {
+      card.errors.push({ level: 0, detail: '鏁版嵁婧愪腑鏃犲彲鐢ㄨ剼鏈紒'})
+    } else if (!card.setting.primaryKey) {
+      card.errors.push({ level: 0, detail: '鏈缃富閿紒'})
+    } else if (!card.setting.subKey) {
+      card.errors.push({ level: 0, detail: '鏈缃瓙琛ㄤ富閿紒'})
+    } else if (!card.setting.subBID) {
+      card.errors.push({ level: 0, detail: '鏈缃瓙琛˙ID锛�'})
+    } else if (!columns.includes(card.setting.primaryKey)) {
+      card.errors.push({ level: 0, detail: '涓婚敭宸插け鏁堬紒'})
+    } else if (!card.setting.supModule) {
+      card.errors.push({ level: 0, detail: '鏈缃笂绾х粍浠讹紒'})
+    }
+    
+    if (card.errors.length === 0) {
+      card.$tables = getTables(card)
+    }
+
+    card.action.forEach(cell => {
+      if (cell.OpenType === 'pop' || (cell.OpenType === 'funcbutton' && cell.execMode === 'pop')) {
+        if (!cell.modal || cell.modal.fields.length === 0) {
+          card.errors.push({ level: 1, detail: `鎸夐挳鈥�${cell.label}鈥濅腑琛ㄥ崟灏氭湭娣诲姞`})
+        }
+      }
+    })
+
+    card.subcards.forEach((item, i) => {
+      let linkbtn = item.setting.linkbtn || ''
+      item.elements.forEach(cell => {
+        if (cell.eleType === 'button') {
+          if (cell.OpenType === 'pop' || (cell.OpenType === 'funcbutton' && cell.execMode === 'pop')) {
+            if (!cell.modal || cell.modal.fields.length === 0) {
+              card.errors.push({ level: 1, detail: `鎸夐挳鈥�${cell.label}鈥濅腑琛ㄥ崟灏氭湭娣诲姞`})
+            }
+          }
+          if (linkbtn && linkbtn === cell.uuid) {
+            linkbtn = ''
+          }
+        } else if (cell.datatype === 'dynamic' && cell.field && !columns.includes(cell.field)) {
+          card.errors.push({ level: 1, detail: `鍗$墖涓姩鎬佸瓧娈碘��${cell.field}鈥濇棤鏁坄})
+        }
+      })
+
+      item.backElements.forEach(cell => {
+        if (cell.eleType === 'button') {
+          if (cell.OpenType === 'pop' || (cell.OpenType === 'funcbutton' && cell.execMode === 'pop')) {
+            if (!cell.modal || cell.modal.fields.length === 0) {
+              card.errors.push({ level: 1, detail: `鎸夐挳鈥�${cell.label}鈥濅腑琛ㄥ崟灏氭湭娣诲姞`})
+            }
+          }
+          if (linkbtn && linkbtn === cell.uuid) {
+            linkbtn = ''
+          }
+        } else if (cell.datatype === 'dynamic' && cell.field && !columns.includes(cell.field)) {
+          card.errors.push({ level: 1, detail: `鍗$墖涓姩鎬佸瓧娈碘��${cell.field}鈥濇棤鏁坄})
+        }
+      })
+
+      if (linkbtn) {
+        card.errors.push({ level: 1, detail: `绗�${i + 1}寮犲崱鐗囦腑缁戝畾鎸夐挳宸插垹闄})
+      }
+    })
+
+    this.setState({
+      card: card
+    })
+
+    this.props.updateConfig(card)
+  }
+
+  /**
+   * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
+   */
+  updateCard = (cell, btn) => {
+    let card = fromJS(this.state.card).toJS()
+
+    card.subcards = card.subcards.map(item => {
+      if (item.uuid === cell.uuid) return cell
+      return item
+    })
+
+    if (btn) {
+      card.action = card.action.filter(item => item.uuid !== btn.uuid)
+    }
+
+    this.updateComponent(card)
+  }
+
+  /**
+   * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
+   */
+  deleteCard = (cell) => {
+    let card = fromJS(this.state.card).toJS()
+    let _this = this
+
+    confirm({
+      content: '纭畾鍒犻櫎鍗$墖鍚楋紵',
+      onOk() {
+        card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
+
+        _this.updateComponent(card)
+      },
+      onCancel() {}
+    })
+  }
+
+  changeStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', ['background', 'height', 'border', 'padding', 'margin', 'shadow', 'clear', 'minHeight'], card.style, this.getStyle)
+  }
+
+  getStyle = (style) => {
+    let _card = {...this.state.card, style}
+    
+    this.updateComponent(_card)
+  }
+
+  addSearch = (copy) => {
+    const { card } = this.state
+
+    let newcard = {}
+
+    if (copy) {
+      newcard = copy
+      newcard.focus = true
+    } else {
+      newcard.uuid = Utils.getuuid()
+      newcard.focus = true
+  
+      newcard.label = 'label'
+      newcard.type = 'select'
+      newcard.resourceType = '0'
+      newcard.options = []
+      newcard.orderType = 'asc'
+      newcard.match = '='
+    }
+
+    // 娉ㄥ唽浜嬩欢-娣诲姞鎼滅储
+    MKEmitter.emit('addSearch', card.uuid, newcard)
+  }
+
+  addButton = (copy) => {
+    const { card } = this.state
+
+    let newcard = {}
+
+    if (copy) {
+      newcard = copy
+      newcard.focus = true
+    } else {
+      newcard.uuid = Utils.getuuid()
+      newcard.focus = true
+      
+      newcard.label = 'label'
+      newcard.Ot = 'requiredSgl'
+      newcard.OpenType = 'pop'
+      newcard.icon = ''
+      newcard.class = 'green'
+      newcard.intertype = card.setting.interType || 'system'
+      newcard.innerFunc = card.setting.innerFunc || ''
+      newcard.sysInterface = card.setting.sysInterface || ''
+      newcard.outerFunc = card.setting.outerFunc || ''
+      newcard.interface = card.setting.interface || ''
+      newcard.execSuccess = 'grid'
+      newcard.execError = 'never'
+      newcard.verify = null
+      newcard.show = 'button'
+      newcard.style = {marginRight: '15px'}
+    }
+
+    // 娉ㄥ唽浜嬩欢-娣诲姞鎸夐挳
+    MKEmitter.emit('addButton', card.uuid, newcard)
+  }
+
+  setSubConfig = (item) => {
+    const { card, appType } = this.state
+    let btn = fromJS(item).toJS()
+
+    if (btn.OpenType === 'pop' || btn.execMode === 'pop') {
+      if (!btn.modal) {
+        btn.modal = {
+          setting: { title: btn.label, width: appType === 'mob' ? 100 : 60, cols: '2', container: 'view', focus: '', finish: 'close', clickouter: 'unclose', display: 'modal' },
+          tables: [],
+          groups: [],
+          fields: []
+        }
+      }
+      MKEmitter.emit('changeModal', card, btn)
+    } else if (btn.OpenType === 'popview' && appType !== 'mob') {
+      MKEmitter.emit('changePopview', card, btn)
+    }
+  }
+
+  addCard = (copy) => {
+    let card = fromJS(this.state.card).toJS()
+    let newcard = {}
+
+    if (copy) { // 绮樿创
+      newcard = copy
+    } else {
+      let height = card.subcards[0].style.height
+      if (height === 'auto') {
+        height = '100px'
+      }
+  
+      newcard = {
+        uuid: Utils.getuuid(),
+        $cardType: 'extendCard',
+        setting: { width: 24, click: ''},
+        style: {
+          height,
+          borderWidth: '1px', borderColor: '#e8e8e8',
+          paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
+          marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
+        },
+        backStyle: {},
+        elements: [],
+        backElements: []
+      }
+    }
+
+    card.subcards.push(newcard)
+
+    this.updateComponent(card)
+  }
+
+  move = (item, direction) => {
+    let card = fromJS(this.state.card).toJS()
+
+    let dragIndex = card.subcards.findIndex(c => c.uuid === item.uuid)
+    let hoverIndex = null
+
+    if (direction === 'left') {
+      hoverIndex = dragIndex - 1
+    } else {
+      hoverIndex = dragIndex + 1
+    }
+
+    if (hoverIndex === -1 || hoverIndex === card.subcards.length) return 
+
+    card.subcards.splice(hoverIndex, 0, ...card.subcards.splice(dragIndex, 1))
+
+    this.updateComponent(card)
+  }
+
+  getWrapForms = () => {
+    const { card } = this.state
+    
+    return getWrapForm(card.wrap, card.columns, card.setting)
+  }
+
+  updateWrap = (res) => {
+    const { card } = this.state
+
+    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
+    }
+
+    
+    if (res.layout === 'flex') {
+      _card.wrap.pagestyle = 'page'
+    }
+    
+    if (res.selStyle === 'tabs' && card.wrap.selStyle !== 'tabs') {
+      Object.keys(_card.style).forEach(key => {
+        if (/^border/.test(key)) {
+          delete _card.style[key]
+        }
+      })
+      _card.style.borderBottomColor = '#eeeeee'
+      _card.style.borderBottomWidth = '1px'
+      _card.style.paddingBottom = '0px'
+
+      _card.subcards.forEach(item => {
+        delete item.style.marginBottom
+      })
+      
+      this.setState({card: {..._card, subcards: []}}, () => {
+        this.updateComponent(_card)
+      })
+    } else {
+      this.updateComponent(_card)
+    }
+  }
+
+  pasteComponent = (res, resolve) => {
+    const { card, appType } = this.state
+
+    let type = res.copyType
+    delete res.copyType
+
+    if (type === 'cardcell') {
+      res.uuid = Utils.getuuid()
+      res.setting = res.setting || {}
+      res.$cardType = 'extendCard'
+      res.setting.width = res.setting.width || 6
+
+      let mobtypes = ['pop', 'prompt', 'exec', 'innerpage', 'funcbutton']
+
+      let elements = []
+      res.elements && res.elements.forEach(cell => {
+        // if (cell.datatype === 'dynamic') {
+        //   cell.datatype = 'static'
+        // }
+
+        if (cell.eleType !== 'button') {
+          cell.uuid = Utils.getuuid()
+          elements.push(cell)
+        } else if (appType === 'mob' && !mobtypes.includes(cell.OpenType)) {
+          return
+        } else {
+          cell.uuid = Utils.getuuid()
+          elements.push(cell)
+        }
+      })
+
+      res.elements = elements
+
+      let backElements = []
+
+      if (appType !== 'mob') {
+        res.backElements && res.backElements.forEach(cell => {
+          // if (cell.datatype === 'dynamic') {
+          //   cell.datatype = 'static'
+          // }
+
+          if (cell.eleType !== 'button') {
+            cell.uuid = Utils.getuuid()
+            backElements.push(cell)
+          } else if (appType === 'mob' && !mobtypes.includes(cell.OpenType)) {
+            return
+          } else {
+            cell.uuid = Utils.getuuid()
+            backElements.push(cell)
+          }
+        })
+      }
+
+      res.backElements = backElements
+
+      resolve({status: true})
+
+      this.addCard(res)
+    } else if (type === 'search' || type === 'form') {
+      if (appType === 'mob') {
+        resolve({status: false, message: '绉诲姩绔暟鎹崱涓嶆敮鎸佹坊鍔犳悳绱㈡潯浠躲��'})
+      } else {
+        res.uuid = Utils.getuuid()
+        let keys = card.search.map(item => item.field.toLowerCase())
+  
+        if (type === 'form') {
+          if (['number', 'switch', 'textarea', 'fileupload', 'hint', 'color', 'funcvar'].includes(res.type)) {
+            res.type = 'text'
+          } else if (res.type === 'radio') {
+            res.type = 'select'
+          } else if (res.type === 'checkbox') {
+            res.type = 'multiselect'
+          } else if (res.type === 'datetime') {
+            res.type = 'date'
+          }
+        }
+  
+        if (res.field && keys.includes(res.field.toLowerCase())) {
+          resolve({status: false, message: '鎼滅储瀛楁宸插瓨鍦紒'})
+          return
+        }
+
+        resolve({status: true})
+
+        this.addSearch(res)
+      }
+    } else if (type === 'action') {
+      if (appType === 'mob' && !['pop', 'prompt', 'exec', 'innerpage'].includes(res.OpenType)) {
+        resolve({status: false, message: '绉诲姩绔笉鏀寔姝ょ被鍨嬬殑鎸夐挳銆�'})
+      } else {
+        resolve({status: true})
+    
+        res.uuid = Utils.getuuid()
+        this.addButton(res)
+      }
+    }
+  }
+
+  clickComponent = (e) => {
+    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
+      e.stopPropagation()
+      MKEmitter.emit('clickComponent', this.state.card.uuid, null, (style) => {
+        let _card = {...this.state.card}
+        _card.style = {..._card.style, ...style}
+
+        this.setState({ card: _card })
+        this.props.updateConfig(_card)
+      })
+    }
+  }
+
+  render() {
+    const { card, appType } = this.state
+
+    let _style = resetStyle(card.style)
+
+    let extraName = ''
+    card.subcards.forEach(subcard => {
+      if (subcard.$cardType === 'extendCard') return
+      if (subcard.setting.controlIcon === 'left') {
+        extraName = ' mk-control-icon'
+      }
+    })
+
+    return (
+      <div className={'menu-double-data-card-edit-box ' + appType} style={_style} onClick={this.clickComponent} id={card.uuid}>
+        <NormalHeader config={card} updateComponent={this.updateComponent}/>
+        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+          <div className="mk-popover-control">
+            <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}>
+              <EditOutlined style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
+            <CopyComponent type="datacard" card={card}/>
+            <PasteComponent options={['action', 'search', 'form', 'cardcell']} updateConfig={this.pasteComponent} />
+            <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle}/>
+            <ClockComponent config={card} updateConfig={this.updateComponent}/>
+            <UserComponent config={card}/>
+            <DeleteOutlined className="close" title="鍒犻櫎缁勪欢" onClick={() => this.props.deletecomponent(card.uuid)} />
+            <SettingComponent config={card} updateConfig={this.updateComponent} />
+          </div>
+        } trigger="hover">
+          <ToolOutlined />
+        </Popover>
+        <ActionComponent config={card} setSubConfig={this.setSubConfig} updateaction={this.updateComponent}/>
+        <div className={'select-' + card.wrap.selStyle + extraName}>
+          {card.subcards.map(subcard => {
+            if (subcard.$cardType === 'extendCard') {
+              return (<CardComponent key={subcard.uuid} cards={card} card={subcard} move={this.move} updateElement={this.updateCard} deleteElement={this.deleteCard}/>)
+            } else {
+              return (<DoubleCardComponent key={subcard.uuid} cards={card} card={subcard} updateElement={this.updateCard}/>)
+            }
+          })}
+        </div>
+        <div style={{clear: 'both'}}></div>
+        {card.wrap.pagestyle === 'page' && card.setting.laypage === 'true' && appType !== 'mob' ? <Pagination total={85} size="small" showTotal={total => `鍏� ${total} 鏉} pageSize={20} defaultCurrent={1}/> : null}
+        {card.wrap.pagestyle === 'page' && card.setting.laypage === 'true' && appType === 'mob' ? <MobPagination /> : null}
+        {card.wrap.pagestyle === 'more' && card.setting.laypage === 'true' ? <div className="mk-more">鏌ョ湅鏇村<DownOutlined/></div> : null}
+        <div className="component-name">
+          <div className="center">
+            <div className="title" onDoubleClick={() => {
+              let oInput = document.createElement('input')
+              oInput.value = card.uuid
+              document.body.appendChild(oInput)
+              oInput.select()
+              document.execCommand('Copy')
+              document.body.removeChild(oInput)
+              message.success('澶嶅埗鎴愬姛銆�')
+            }}>{card.name}</div>
+            <div className="content">
+              {card.errors && card.errors.map((err, index) => {
+                if (err.level === 0) {
+                  return <span key={index} className="error">{err.detail}</span>
+                } else {
+                  return <span key={index} className="waring">{err.detail}锛�</span>
+                }
+              })}
+            </div>
+          </div>
+        </div>
+      </div>
+    )
+  }
+}
+
+export default DoubleDataCardEditComponent
\ No newline at end of file
diff --git a/src/menu/components/card/double-data-card/index.scss b/src/menu/components/card/double-data-card/index.scss
new file mode 100644
index 0000000..40ea29c
--- /dev/null
+++ b/src/menu/components/card/double-data-card/index.scss
@@ -0,0 +1,148 @@
+.menu-double-data-card-edit-box {
+  position: relative;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  min-height: 20px;
+  overflow-y: auto;
+  
+  >.anticon-tool {
+    position: absolute;
+    z-index: 2;
+    font-size: 16px;
+    right: 1px;
+    top: 1px;
+    cursor: pointer;
+    padding: 5px;
+    background: rgba(255, 255, 255, 0.55);
+  }
+
+  .card-item, .card-item-wrap {
+    overflow: hidden;
+    position: relative;
+    background-color: #ffffff;
+    background-position: center center;
+    background-repeat: no-repeat;
+    background-size: cover;
+    min-height: 20px;
+  }
+  .card-item.hover:not(:hover) {
+    button {
+      opacity: 0;
+      transition: opacity 0.3s;
+    }
+  }
+  
+  .card-item:hover {
+    box-shadow: 0px 0px 2px #1890ff;
+  }
+
+  .model-menu-card-cell-list .card-detail-row > .anticon-plus {
+    position: absolute;
+    right: -30px;
+    font-size: 16px;
+  }
+  .model-menu-action-list {
+    line-height: 40px;
+    .ant-row > .anticon-plus {
+      position: absolute;
+      right: -30px;
+      font-size: 16px;
+    }
+  }
+  .ant-pagination {
+    float: right;
+    margin: 10px;
+  }
+
+  .model-menu-action-list {
+    .page-card {
+      line-height: 55px;
+    }
+  }
+  .normal-pagination {
+    .am-button::before {
+      display: none;
+    }
+    .am-button {
+      border: none;
+      font-size: 16px;
+    }
+  }
+  // .float-center {
+  //   text-align: center;
+  //   >.ant-col {
+  //     display: inline-block;
+  //     float: none;
+  //     text-align: left;
+  //     vertical-align: top;
+  //   }
+  // }
+  // .float-right {
+  //   text-align: right;
+  //   >.ant-col {
+  //     display: inline-block;
+  //     float: none;
+  //     text-align: left;
+  //     vertical-align: top;
+  //   }
+  // }
+  .select-tabs {
+    .card-item {
+      border-top: none!important;
+      border-left: none!important;
+      border-right: none!important;
+      border-radius: 0px!important;
+      border-bottom: 2px solid transparent!important;
+    }
+  }
+  .mk-more {
+    text-align: center;
+    line-height: 40px;
+    .anticon-down {
+      margin-left: 2px;
+    }
+  }
+  .select-check.mk-control-icon {
+    .mk-extend-card {
+      >.circle-select {
+        margin-right: 35px;
+      }
+    }
+  }
+}
+
+.menu-double-data-card-edit-box::-webkit-scrollbar {
+  width: 7px;
+  height: 7px;
+}
+.menu-double-data-card-edit-box::-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);
+}
+.menu-double-data-card-edit-box::-webkit-scrollbar-track {
+  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+  border-radius: 3px;
+  border: 1px solid rgba(0, 0, 0, 0.07);
+  background: rgba(0, 0, 0, 0);
+}
+.menu-double-data-card-edit-box::after {
+  display: block;
+  content: ' ';
+  clear: both;
+}
+.menu-double-data-card-edit-box.mob {
+  .model-menu-action-list {
+    .page-card {
+      line-height: 40px;
+      margin-top: 5px;
+    }
+  }
+}
+.menu-double-data-card-edit-box:hover {
+  z-index: 1;
+  box-shadow: 0px 0px 4px #1890ff;
+}
diff --git a/src/menu/components/card/double-data-card/options.jsx b/src/menu/components/card/double-data-card/options.jsx
new file mode 100644
index 0000000..704da63
--- /dev/null
+++ b/src/menu/components/card/double-data-card/options.jsx
@@ -0,0 +1,240 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (wrap, columns = [], setting) {
+  let appType = sessionStorage.getItem('appType')
+  // let MenuType = ''
+  // let menu = window.GLOB.customMenu
+  let laypage = setting && setting.laypage !== 'false'
+
+  // if (menu.parentId === 'BillPrintTemp') {
+  //   MenuType = 'billPrint'
+  // }
+
+  let roleList = sessionStorage.getItem('sysRoles')
+
+  if (roleList) {
+    try {
+      roleList = JSON.parse(roleList)
+    } catch (e) {
+      roleList = []
+    }
+  } else {
+    roleList = []
+  }
+
+  let menulist = []
+
+  if (appType === 'mob') {
+    menulist = sessionStorage.getItem('appMenus')
+    if (menulist) {
+      try {
+        menulist = JSON.parse(menulist)
+      } catch (e) {
+        menulist = []
+      }
+    } else {
+      menulist = []
+    }
+  }
+
+  const cardWrapForm = [
+    {
+      type: 'text',
+      field: 'title',
+      label: '鏍囬',
+      initval: wrap.title || '',
+      required: false
+    },
+    {
+      type: 'text',
+      field: 'name',
+      label: '缁勪欢鍚嶇О',
+      initval: wrap.name || '',
+      tooltip: '鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�',
+      required: true
+    },
+    {
+      type: 'number',
+      field: 'width',
+      label: '瀹藉害',
+      initval: wrap.width || 24,
+      tooltip: '鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��',
+      min: 1,
+      max: 24,
+      precision: 0,
+      required: true
+    },
+    // {
+    //   type: 'radio',
+    //   field: 'layout',
+    //   label: '鍗$墖甯冨眬',
+    //   initval: wrap.layout || 'grid',
+    //   tooltip: appType === 'mob' ? '寮规�у竷灞�鏃讹紝婊戝姩鍔犺浇鏃犳晥' : '',
+    //   required: false,
+    //   options: [
+    //     {value: 'grid', label: '鏍呮牸甯冨眬'},
+    //     {value: 'flex', label: '寮规�у竷灞�'},
+    //   ],
+    //   controlFields: [
+    //     {field: 'printHeight', values: ['flex']},
+    //     {field: 'cardFloat', values: ['grid']},
+    //   ]
+    // },
+    {
+      type: 'radio',
+      field: 'pagestyle',
+      label: '鍒嗛〉椋庢牸',
+      initval: wrap.pagestyle || 'page',
+      tooltip: '鏁版嵁婧愰�夋嫨鍒嗛〉鏃舵湁鏁堛�傛敞锛氬脊鎬у竷灞�鏃跺浐瀹氫负椤电爜銆�',
+      required: false,
+      disabled: !laypage,
+      options: [
+        {value: 'page', label: '椤电爜'},
+        {value: 'slide', label: '婊戝姩鍔犺浇', forbid: appType !== 'mob' || sessionStorage.getItem('editMenuType') === 'popview'},
+        {value: 'more', label: '鏌ョ湅鏇村'},
+      ],
+      controlFields: [
+        {field: 'slidetip', values: ['slide']},
+      ],
+    },
+    {
+      type: 'radio',
+      field: 'cardType',
+      label: '鏁版嵁閫夋嫨',
+      initval: wrap.cardType || '',
+      required: false,
+      options: [
+        {value: '', label: '涓嶅彲閫�'},
+        {value: 'radio', label: '鍗曢��'},
+        {value: 'checkbox', label: '澶氶��'},
+      ],
+      controlFields: [
+        {field: 'selected', values: ['radio', 'checkbox']},
+        {field: 'selStyle', values: ['radio', 'checkbox']},
+      ],
+    },
+    {
+      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: 'select',
+      field: 'selStyle',
+      label: '閫変腑椋庢牸',
+      initval: wrap.selStyle || 'active',
+      tooltip: '瀛樺湪杈规鏃讹紝杈规浼氫娇鐢ㄧ郴缁熻壊銆�',
+      required: false,
+      options: [
+        {value: 'none', label: '鏃�'},
+        {value: 'active', label: '澶栭槾褰�'},
+        {value: 'backFont', label: '鑳屾櫙+鏂囧瓧'},
+        {value: 'font', label: '鏂囧瓧'},
+        {value: 'check', label: '鍕鹃�夛紙鍦嗘锛�'},
+        {value: 'check square', label: '鍕鹃�夛紙鏂规锛�'}
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'parity',
+      label: '濂囧伓鑳屾櫙',
+      initval: wrap.parity || 'false',
+      tooltip: '鍋舵暟琛屼細娣诲姞鑳屾櫙鑹层��',
+      required: false,
+      options: [
+        {value: 'false', label: '鏃�'},
+        {value: 'true', label: '鏈�'},
+      ],
+    },
+    // {
+    //   type: 'number',
+    //   field: 'printHeight',
+    //   label: '鎹㈢畻楂樺害',
+    //   initval: wrap.printHeight || '',
+    //   tooltip: '褰撳墠鏁版嵁鍗¢珮搴︾浉褰撲簬鍑犳潯鏁版嵁銆�',
+    //   required: false,
+    //   forbid: MenuType !== 'billPrint'
+    // },
+    {
+      type: 'radio',
+      field: 'empty',
+      label: '绌哄�奸殣钘�',
+      initval: wrap.empty || 'show',
+      tooltip: '褰撴煡璇㈡暟鎹负绌烘椂锛岄殣钘忚缁勪欢銆�',
+      required: false,
+      options: [
+        {value: 'show', label: '鍚�'},
+        {value: 'hidden', label: '鏄�'},
+      ],
+    },
+    {
+      type: 'select',
+      field: 'controlField',
+      label: '绂佺敤瀛楁',
+      initval: wrap.controlField || '',
+      tooltip: '鐢ㄤ簬鎺у埗琛屾暟鎹槸鍚﹀彲閫夋嫨銆�',
+      required: false,
+      allowClear: true,
+      options: columns,
+      controlFields: [
+        {field: 'controlVal', notNull: true},
+      ],
+    },
+    {
+      type: 'text',
+      field: 'controlVal',
+      label: '绂佺敤鍊�',
+      initval: wrap.controlVal || '',
+      tooltip: '褰撳瓧娈靛�间笌绂佺敤鍊肩浉绛夋椂锛岃鏁版嵁浼氱鐢紝澶氫釜鍊肩敤閫楀彿鍒嗛殧銆�',
+      required: false
+    },
+    {
+      type: 'radio',
+      field: 'permission',
+      label: '鏉冮檺楠岃瘉',
+      initval: wrap.permission || 'false',
+      required: false,
+      options: [
+        {value: 'true', label: '鍚敤'},
+        {value: 'false', label: '绂佺敤'},
+      ],
+      forbid: !appType || sessionStorage.getItem('editMenuType') === 'popview'
+    },
+    {
+      type: 'multiselect',
+      field: 'blacklist',
+      label: '榛戝悕鍗�',
+      initval: wrap.blacklist || [],
+      required: false,
+      options: roleList,
+      forbid: !!appType
+    },
+    {
+      type: 'text',
+      field: 'slidetip',
+      label: '搴曢儴鎻愮ず',
+      initval: wrap.slidetip || wrap.slidetip === '' ? wrap.slidetip : '娌℃湁鏇村浜�',
+      tooltip: '婊戝姩鍔犺浇鑷冲簳閮ㄦ椂鐨勬彁绀轰俊鎭��',
+      required: false,
+      forbid: !laypage || appType !== 'mob'
+    },
+  ]
+
+  return cardWrapForm.filter(item => {
+    if (['pagestyle'].includes(item.field)) {
+      item.options = item.options.filter(option => !option.forbid)
+    }
+
+    return !item.forbid
+  })
+} 
\ No newline at end of file
diff --git a/src/menu/components/card/doublecardcomponent/index.jsx b/src/menu/components/card/doublecardcomponent/index.jsx
new file mode 100644
index 0000000..f6d1544
--- /dev/null
+++ b/src/menu/components/card/doublecardcomponent/index.jsx
@@ -0,0 +1,318 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Popover, Col } from 'antd'
+import { UpOutlined, PlusOutlined, PlusSquareOutlined, EditOutlined, ToolOutlined, FontColorsOutlined } from '@ant-design/icons'
+
+import asyncComponent from '@/utils/asyncComponent'
+import asyncIconComponent from '@/utils/asyncIconComponent'
+import { resetStyle } from '@/utils/utils-custom.js'
+import getSettingForm from './options'
+import Utils from '@/utils/utils.js'
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const CardCellComponent = asyncComponent(() => import('@/menu/components/card/cardcellcomponent'))
+const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const PasteController = asyncIconComponent(() => import('@/components/paste'))
+
+class DoubleCardBoxComponent extends Component {
+  static propTpyes = {
+    cards: PropTypes.object,         // 鍗$墖琛岄厤缃俊鎭�
+    card: PropTypes.object,          // 鍗$墖閰嶇疆淇℃伅
+    updateElement: PropTypes.func    // 鑿滃崟閰嶇疆鏇存柊
+  }
+
+  state = {
+    card: null,            // 鍗$墖淇℃伅锛屽寘鎷鍙嶉潰
+    appType: sessionStorage.getItem('appType'),
+    visible: false,
+    side: ''
+  }
+
+  /**
+   * @description 鎼滅储鏉′欢鍒濆鍖�
+   */
+  UNSAFE_componentWillMount () {
+    const { card } = this.props
+
+    this.setState({
+      card: fromJS(card).toJS()
+    })
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    const { cards } = this.props
+    
+    return !is(fromJS(cards), fromJS(nextProps.cards)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  getStyle = (style) => {
+    const { card, side } = this.state
+
+    let _card = fromJS(card).toJS()
+    if (side === 'sub') {
+      _card.backStyle = style
+    } else {
+      _card.style = style
+    }
+
+    this.setState({
+      card: _card
+    })
+
+    this.props.updateElement(_card)
+  }
+
+  updateCard = (elements, btn, type) => {
+    const { card } = this.state
+
+    let _card = {}
+
+    if (type === 'sub') {
+      _card = {...card, backElements: elements}
+    } else {
+      _card = {...card, elements: elements}
+    }
+
+    this.setState({
+      card: _card
+    })
+
+    this.props.updateElement(_card, btn)
+  }
+  
+  addElement = (type) => {
+    const { card } = this.state
+
+    let newcard = {}
+    newcard.uuid = Utils.getuuid()
+    newcard.focus = true
+    
+    newcard.eleType = 'text'
+    newcard.datatype = 'dynamic'
+    newcard.height = 1
+
+    // 娉ㄥ唽浜嬩欢-娣诲姞鍏冪礌
+    MKEmitter.emit('cardAddElement', card.uuid, newcard, type)
+  }
+
+  addButton = (type) => {
+    const { card } = this.state
+
+    let newcard = {eleType: 'button', label: 'button', verify: null, show: 'link', sqlType: '', Ot: 'requiredSgl', OpenType: 'prompt', icon: '', class: 'primary', intertype: 'system', execSuccess: 'grid', execError: 'never', popClose: 'never'}
+    newcard.uuid = Utils.getuuid()
+    newcard.focus = true
+
+    // 娉ㄥ唽浜嬩欢-娣诲姞鍏冪礌
+    MKEmitter.emit('cardAddElement', card.uuid, newcard, type)
+  }
+
+  changeStyle = (type) => {
+    const { card } = this.state
+
+    let _style = null
+    let options = ['height', 'background', 'border', 'padding', 'margin', 'shadow']
+
+    if (type === 'sub') {
+      _style = fromJS(card.backStyle).toJS()
+    } else {
+      _style = fromJS(card.style).toJS()
+    }
+
+    this.setState({side: type})
+
+    MKEmitter.emit('changeStyle', options, _style, this.getStyle)
+  }
+
+  getSettingForms = () => {
+    const { cards } = this.props
+    const { card } = this.state
+
+    let buttons = []
+    card.elements.forEach(item => {
+      if (item.eleType === 'button') {
+        buttons.push({
+          value: item.uuid,
+          label: item.label
+        })
+      }
+    })
+
+    return getSettingForm(card.setting, buttons, cards.columns, 'main')
+  }
+
+  getBackSettingForms = () => {
+    const { cards } = this.props
+    const { card } = this.state
+
+    let buttons = []
+    card.backElements.forEach(item => {
+      if (item.eleType === 'button') {
+        buttons.push({
+          value: item.uuid,
+          label: item.label
+        })
+      }
+    })
+
+    return getSettingForm(card.backSetting, buttons, cards.subColumns)
+  }
+
+  updateSetting = (res, type) => {
+    const { card, appType } = this.state
+
+    if (appType === '' && res.menu) {
+      let list = null
+      try {
+        list = JSON.parse(sessionStorage.getItem('thdMenuList')) || []
+      } catch (e) {
+        list = []
+      }
+
+      let id = res.menu[res.menu.length - 1]
+
+      list.forEach(item => {
+        if (item.MenuID === id) {
+          res.MenuID = id
+          res.MenuName = item.MenuName
+          res.MenuNo = item.MenuNo
+          res.tabType = item.type
+        }
+      })
+    }
+
+    let _card = {...card}
+
+    if (type === 'sub') {
+      _card.backSetting = res
+    } else {
+      _card.setting = res
+    }
+
+    this.setState({
+      card: _card
+    })
+
+    this.props.updateElement(_card)
+  }
+
+  paste = (element, resolve, type) => {
+    const { card } = this.state
+
+    let _uuid = Utils.getuuid()
+    
+    if (element.copyType === 'action') {
+      element.eleType = 'button'
+    }
+
+    element.uuid = _uuid
+    element.focus = true
+
+    resolve({status: true})
+
+    // 娉ㄥ唽浜嬩欢-娣诲姞鍏冪礌
+    MKEmitter.emit('cardAddElement', card.uuid, element, type)
+  }
+
+  doubleClickCard = (type) => {
+    const { card } = this.state
+
+    if (type !== 'sub' && card.setting.click === 'menu' && card.setting.menu) {
+      MKEmitter.emit('changeEditMenu', {MenuID: card.setting.menu})
+    } else if (type === 'sub' && card.backSetting.click === 'menu' && card.backSetting.menu) {
+      MKEmitter.emit('changeEditMenu', {MenuID: card.backSetting.menu})
+    }
+  }
+
+  render() {
+    const { cards } = this.props
+    const { card } = this.state
+
+    let _style = {...card.style}
+    let _backStyle = {...card.backStyle}
+    let _wrapStyle = {}
+
+    _style = resetStyle(_style)
+    _backStyle = resetStyle(_backStyle)
+
+    if (card.setting.position === 'inner') {
+      Object.keys(_style).forEach(key => {
+        if (!/^(margin|border|box)/.test(key)) return
+        _wrapStyle[key] = _style[key]
+        delete _style[key]
+      })
+    }
+
+    let checkAll = ''
+    let mainBox = ''
+    if (cards.wrap.selStyle === 'check') {
+      checkAll = 'mk-checkable'
+    } else if (cards.wrap.selStyle === 'check square') {
+      checkAll = 'mk-checkable square'
+    } else if (card.setting.display !== 'default' && card.setting.controlIcon === 'left') {
+      mainBox = 'flex-card '
+    }
+
+    return (
+      <Col span={card.setting.width || 24}>
+        <div className="card-item-wrap" style={_wrapStyle}>
+          <div className={`card-item ${card.setting.btnControl || ''} ${checkAll} mk-${card.setting.display} ${mainBox}`} style={_style} onDoubleClick={(e) => {e.stopPropagation(); this.doubleClickCard()}} id={card.uuid}>
+            <span className="circle-select"></span>
+            {card.setting.controlIcon === 'left' ? <PlusSquareOutlined /> : <UpOutlined />}
+            <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">
+                  <PlusOutlined className="plus" title="娣诲姞鍏冪礌" onClick={() => this.addElement('main')} />
+                  <PlusSquareOutlined className="plus" title="娣诲姞鎸夐挳" onClick={() => this.addButton('main')} />
+                  <NormalForm title={'寰幆鍗$墖璁剧疆'} width={950} update={(res) => this.updateSetting(res)} getForms={this.getSettingForms}>
+                    <EditOutlined className="edit" title="缂栬緫"/>
+                  </NormalForm>
+                  <CopyComponent type="cardcell" card={card}/>
+                  <PasteController options={['action', 'customCardElement']} 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)}/>
+          </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}>
+              <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">
+                    <PlusOutlined className="plus" title="娣诲姞鍏冪礌" onClick={() => this.addElement('sub')} />
+                    <PlusSquareOutlined className="plus" title="娣诲姞鎸夐挳" onClick={() => this.addButton('sub')} />
+                    <NormalForm title={'寰幆瀛愬崱鐗囪缃�'} width={950} update={(res) => this.updateSetting(res, 'sub')} getForms={this.getBackSettingForms}>
+                      <EditOutlined className="edit" title="缂栬緫"/>
+                    </NormalForm>
+                    <CopyComponent type="cardcell" card={card}/>
+                    <PasteController options={['action', 'customCardElement']} 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')}/>
+            </div>
+          </Col>
+        </div>
+      </Col>
+    )
+  }
+}
+
+export default DoubleCardBoxComponent
\ No newline at end of file
diff --git a/src/menu/components/card/doublecardcomponent/index.scss b/src/menu/components/card/doublecardcomponent/index.scss
new file mode 100644
index 0000000..344ab73
--- /dev/null
+++ b/src/menu/components/card/doublecardcomponent/index.scss
@@ -0,0 +1,118 @@
+.menus-detail-modal {
+  .ant-modal {
+    top: 70px;
+  }
+  .ant-modal-body {
+    min-height: 200px;
+    .menu-line {
+      display: flex;
+      div {
+        padding: 16px 16px;
+        border-top: 1px solid #e8e8e8;
+        border-left: 1px solid #e8e8e8;
+        word-break: break-all;
+      }
+      div:last-child {
+        border-right: 1px solid #e8e8e8;
+      }
+      .sort {
+        width: 10%;
+        text-align: center;
+      }
+      .sign {
+        width: 35%;
+      }
+      .name {
+        width: 35%;
+      }
+      .action {
+        width: 20%;
+        text-align: center;
+        span {
+          display: inline-block;
+          padding: 0 10px;
+          cursor: pointer;
+          color: #1890ff;
+        }
+      }
+    }
+    .menu-line:first-child {
+      background-color: #fafafa;
+    }
+    .menu-line:last-child {
+      div {
+        border-bottom: 1px solid #e8e8e8;
+      }
+    }
+  }
+}
+
+.card-control {
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  .anticon-tool {
+    position: absolute;
+    left: 1px;
+    top: 1px;
+    padding: 1px;
+    z-index: 2;
+    font-size: 16px;
+    cursor: pointer;
+    background: rgba(255, 255, 255, 0.55);
+  }
+}
+
+.card-item {
+  >.circle-select {
+    position: relative;
+    display: none;
+    width: 16px;
+    height: 16px;
+    border: 1px solid #cccccc;
+    border-radius: 50%;
+    box-sizing: content-box;
+    margin: auto;
+    margin-right: 5px;
+    background-color: #ffffff;
+    transition: border-color 0.2s;
+    cursor: pointer;
+  }
+  >.anticon-up {
+    position: absolute;
+    bottom: 10px;
+    right: 10px;
+    display: none;
+  }
+  >.anticon-plus-square {
+    margin: auto;
+    width: 30px;
+    font-size: 18px;
+  }
+}
+// square
+.card-item.mk-checkable {
+  display: flex;
+  .circle-select {
+    display: inline-block;
+  }
+  .model-menu-card-cell-list {
+    flex: 1;
+  }
+}
+.card-item.flex-card {
+  display: flex;
+  .model-menu-card-cell-list {
+    flex: 1;
+  }
+}
+.card-item.mk-checkable.square {
+  .circle-select {
+    border-radius: 0;
+  }
+}
+.card-item.mk-unfold, .card-item.mk-collapse {
+  >.anticon-up {
+    display: inline-block;
+  }
+}
diff --git a/src/menu/components/card/doublecardcomponent/options.jsx b/src/menu/components/card/doublecardcomponent/options.jsx
new file mode 100644
index 0000000..38a169d
--- /dev/null
+++ b/src/menu/components/card/doublecardcomponent/options.jsx
@@ -0,0 +1,194 @@
+/**
+ * @description Setting琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (setting, buttons = [], columns, type) {
+  let appType = sessionStorage.getItem('appType')
+  let menulist = []
+
+  if (appType) {
+    menulist = sessionStorage.getItem('appMenus')
+    if (menulist) {
+      try {
+        menulist = JSON.parse(menulist)
+      } catch (e) {
+        menulist = []
+      }
+    } else {
+      menulist = []
+    }
+  } else {
+    menulist = sessionStorage.getItem('fstMenuList')
+    if (menulist) {
+      try {
+        menulist = JSON.parse(menulist)
+      } catch (e) {
+        menulist = []
+      }
+    } else {
+      menulist = []
+    }
+  }
+
+  const cardSettingForm = [
+    {
+      type: 'number',
+      field: 'width',
+      label: '鍗$墖瀹藉害',
+      initval: setting.width || 24,
+      tooltip: '鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��',
+      min: 1,
+      max: 24,
+      precision: 0,
+      required: true
+    },
+    {
+      type: 'radio',
+      field: 'display',
+      label: '瀛愯〃鏄剧ず',
+      initval: setting.display || 'default',
+      tooltip: '灞曞紑涓庡悎骞朵负鐢ㄦ埛鍙嚜琛屽垏鎹€��',
+      required: false,
+      options: [
+        {value: 'default', label: '榛樿'},
+        {value: 'unfold', label: '灞曞紑'},
+        {value: 'collapse', label: '鍚堝苟'},
+      ],
+      controlFields: [
+        // {field: 'position', values: ['default']},
+        {field: 'controlIcon', values: ['unfold', 'collapse']},
+      ],
+      forbid: type !== 'main'
+    },
+    {
+      type: 'radio',
+      field: 'controlIcon',
+      label: '鎺у埗鍥炬爣',
+      initval: setting.controlIcon || 'left',
+      required: false,
+      options: [
+        {value: 'left', label: '宸︿晶'},
+        {value: 'right', label: '鍙充晶'},
+      ],
+      forbid: type !== 'main'
+    },
+    {
+      type: 'radio',
+      field: 'position',
+      label: '瀛愯〃浣嶇疆',
+      initval: setting.position || 'bottom',
+      required: false,
+      options: [
+        {value: 'bottom', label: '涓昏〃涓嬫柟'},
+        {value: 'inner', label: '涓昏〃鍐�'},
+      ],
+      forbid: type !== 'main'
+    },
+    {
+      type: 'select',
+      field: 'bgField',
+      label: '鑳屾櫙鍥�',
+      initval: setting.bgField || '',
+      tooltip: '鍔ㄦ�佽儗鏅紝鑳屾櫙鍥剧墖鐢卞瓧娈靛�兼帶鍒躲�傝娉ㄦ剰璋冩暣鑳屾櫙鏍峰紡銆�',
+      required: false,
+      options: columns
+    },
+    {
+      type: 'select',
+      field: 'click',
+      label: '鐐瑰嚮浜嬩欢',
+      initval: setting.click || '',
+      required: false,
+      options: [
+        {value: '', label: '鏃�'},
+        {value: 'menu', label: '鑿滃崟'},
+        {value: 'link', label: '閾炬帴'},
+        {value: 'button', label: '鎸夐挳'},
+        {value: 'unfold', label: '瀛愯〃缂╂斁', disabled: type !== 'main'},
+      ],
+      controlFields: [
+        {field: 'menu', values: ['menu']},
+        {field: 'linkurl', values: ['link']},
+        {field: 'open', values: ['menu', 'link']},
+        {field: 'joint', values: ['menu', 'link']},
+        {field: 'linkbtn', values: ['button']},
+        {field: 'clickType', values: ['button', 'unfold']},
+      ]
+    },
+    {
+      type: appType ? 'select' : 'cascader',
+      field: 'menu',
+      label: '鍏宠仈鑿滃崟',
+      initval: setting.menu || (appType ? '' : []),
+      required: true,
+      extendName: 'MenuNo',
+      options: menulist,
+    },
+    {
+      type: 'textarea',
+      field: 'linkurl',
+      label: '閾炬帴',
+      initval: setting.linkurl || '',
+      required: true,
+      options: [],
+      span: 24
+    },
+    {
+      type: 'radio',
+      field: 'open',
+      label: '鎵撳紑鏂瑰紡',
+      initval: setting.open || 'blank',
+      required: false,
+      options: [
+        {value: 'blank', label: appType !== 'mob' ? '鏂扮獥鍙�' : '鏂伴〉闈�'},
+        {value: 'self', label: appType !== 'mob' ? '褰撳墠绐楀彛' : '褰撳墠椤甸潰'},
+      ],
+      forbid: appType !== 'pc' && appType !== 'mob'
+    },
+    {
+      type: 'radio',
+      field: 'joint',
+      label: '鍙傛暟鎷兼帴',
+      initval: setting.joint || 'true',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄�'},
+        {value: 'false', label: '鍚�'},
+      ],
+    },
+    {
+      type: 'select',
+      field: 'linkbtn',
+      label: '鍏宠仈鎸夐挳',
+      initval: setting.linkbtn || '',
+      required: true,
+      options: buttons
+    },
+    {
+      type: 'radio',
+      field: 'clickType',
+      label: '瑙﹀彂鏂瑰紡',
+      initval: setting.clickType || 'normal',
+      required: false,
+      options: [
+        {value: 'normal', label: '鍗曞嚮'},
+        {value: 'multi', label: '鍙屽嚮'},
+      ],
+      forbid: appType === 'mob'
+    },
+    {
+      type: 'radio',
+      field: 'btnControl',
+      label: '鎸夐挳鎺у埗',
+      initval: setting.btnControl || 'show',
+      tooltip: '鍙缃寜閽樉绀鸿鍒欙紝涓�鐩存樉绀烘垨榧犳爣鎮诞鏃舵樉绀恒��',
+      required: false,
+      options: [
+        {value: 'show', label: '姝e父鏄剧ず'},
+        {value: 'hover', label: '鎮诞鏄剧ず'},
+      ],
+      forbid: appType === 'mob'
+    }
+  ]
+
+  return cardSettingForm
+} 
\ No newline at end of file
diff --git a/src/menu/components/form/simple-form/index.jsx b/src/menu/components/form/simple-form/index.jsx
index 5848926..0875e25 100644
--- a/src/menu/components/form/simple-form/index.jsx
+++ b/src/menu/components/form/simple-form/index.jsx
@@ -340,35 +340,28 @@
     let index = null
     uniq.set(_form.field, true)
 
-    let _inputIndex = 1
-    let _tabIndex = 1
-    let _linkIndex = 1
     card.subcards[0].fields.forEach((item, i) => {
       if (_form.uuid === item.uuid) {
         index = i
       }
 
-      let label = `${item.field || ''}锛�${item.label}锛塦
-
       if (['text', 'number', 'textarea', 'color'].includes(item.type) && _item.field !== item.field) {
         _inputfields.push({
           field: item.field,
-          label: _inputIndex + '銆�' + label
+          label: item.label
         })
-        _inputIndex++
       }
       if (_form.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type)) {
         _tabfields.push({
           field: item.field,
-          label: _tabIndex + '銆�' + label
+          label: item.label
         })
-        _tabIndex++
       }
 
       if (item.type === 'switch') {
         _linksupFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + label
+          label: item.label
         })
       }
       
@@ -378,19 +371,15 @@
         uniq.set(item.field, true)
 
         _linkableFields.push({
-          value: item.field,
-          text: _linkIndex + '銆�' + item.label + ' (琛ㄥ崟)'
+          field: item.field,
+          label: item.label + '-琛ㄥ崟'
         })
         _linksupFields.push({
-          value: item.field,
-          text: _linkIndex + '銆�' + label
+          field: item.field,
+          label: item.label
         })
-
-        _linkIndex++
       }
     })
-
-    _tabfields.unshift({field: '', label: '鍘熻〃鍗�'})
 
     if (index !== null) {
       if (index === 0) {
@@ -406,9 +395,8 @@
 
         _linkableFields.push({
           field: col.field,
-          label: _linkIndex + '銆�' + col.label + ' (鏄剧ず鍒�)'
+          label: col.label + '-鏄剧ず鍒�'
         })
-        _linkIndex++
       }
     })
 
diff --git a/src/menu/components/form/step-form/index.jsx b/src/menu/components/form/step-form/index.jsx
index a10ff62..b16532c 100644
--- a/src/menu/components/form/step-form/index.jsx
+++ b/src/menu/components/form/step-form/index.jsx
@@ -441,36 +441,29 @@
     let index = null
     uniq.set(_form.field, true)
 
-    let _inputIndex = 1
-    let _tabIndex = 1
-    let _linkIndex = 1
     group.fields.forEach((item, i) => {
       if (_form.uuid === item.uuid) {
         index = i
       }
 
-      let label = `${item.field || ''}锛�${item.label}锛塦
-
       if (['text', 'number', 'textarea', 'color'].includes(item.type) && _item.field !== item.field) {
         _inputfields.push({
           field: item.field,
-          label: _inputIndex + '銆�' + label
+          label: item.label
         })
-        _inputIndex++
       }
 
       if (_item.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type)) {
         _tabfields.push({
           field: item.field,
-          label: _tabIndex + '銆�' + label
+          label: item.label
         })
-        _tabIndex++
       }
 
       if (item.type === 'switch') {
         _linksupFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + label
+          label: item.label
         })
       }
       
@@ -481,17 +474,14 @@
 
         _linkableFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + item.label + ' (琛ㄥ崟)'
+          label: item.label + '-琛ㄥ崟'
         })
         _linksupFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + label
+          label: item.label
         })
-        _linkIndex++
       }
     })
-
-    _tabfields.unshift({field: '', label: '鍘熻〃鍗�'})
 
     if (index !== null) {
       if (index === 0) {
@@ -507,9 +497,8 @@
 
         _linkableFields.push({
           field: col.field,
-          label: _linkIndex + '銆�' + col.label + ' (鏄剧ず鍒�)'
+          label: col.label + '-鏄剧ず鍒�'
         })
-        _linkIndex++
       }
     })
 
diff --git a/src/menu/components/form/tab-form/index.jsx b/src/menu/components/form/tab-form/index.jsx
index b3b5b18..cc18525 100644
--- a/src/menu/components/form/tab-form/index.jsx
+++ b/src/menu/components/form/tab-form/index.jsx
@@ -447,35 +447,28 @@
     let index = null
     uniq.set(_form.field, true)
 
-    let _inputIndex = 1
-    let _tabIndex = 1
-    let _linkIndex = 1
     group.fields.forEach((item, i) => {
       if (_form.uuid === item.uuid) {
         index = i
       }
 
-      let label = `${item.field || ''}锛�${item.label}锛塦
-
       if (['text', 'number', 'textarea', 'color'].includes(item.type) && _item.field !== item.field) {
         _inputfields.push({
           field: item.field,
-          label: _inputIndex + '銆�' + label
+          label: item.label
         })
-        _inputIndex++
       }
       if (_form.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type)) {
         _tabfields.push({
           field: item.field,
-          label: _tabIndex + '銆�' + label
+          label: item.label
         })
-        _tabIndex++
       }
 
       if (item.type === 'switch') {
         _linksupFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + label
+          label: item.label
         })
       }
       
@@ -485,19 +478,15 @@
         uniq.set(item.field, true)
 
         _linkableFields.push({
-          value: item.field,
-          text: _linkIndex + '銆�' + item.label + ' (琛ㄥ崟)'
+          field: item.field,
+          label: item.label + '-琛ㄥ崟'
         })
         _linksupFields.push({
-          value: item.field,
-          text: _linkIndex + '銆�' + label
+          field: item.field,
+          label: item.label
         })
-
-        _linkIndex++
       }
     })
-
-    _tabfields.unshift({field: '', label: '鍘熻〃鍗�'})
 
     if (index !== null) {
       if (index === 0) {
@@ -513,9 +502,8 @@
 
         _linkableFields.push({
           field: col.field,
-          label: _linkIndex + '銆�' + col.label + ' (鏄剧ず鍒�)'
+          label: col.label + '-鏄剧ず鍒�'
         })
-        _linkIndex++
       }
     })
 
diff --git a/src/menu/components/group/groupcomponents/card.jsx b/src/menu/components/group/groupcomponents/card.jsx
index 2c62e1e..7dbdbb0 100644
--- a/src/menu/components/group/groupcomponents/card.jsx
+++ b/src/menu/components/group/groupcomponents/card.jsx
@@ -25,6 +25,7 @@
 const CustomChart = asyncComponent(() => import('@/menu/components/chart/chart-custom'))
 const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline'))
 const AntvG6 = asyncComponent(() => import('@/menu/components/chart/antv-G6'))
+const DoubleDataCard = asyncComponent(() => import('@/menu/components/card/double-data-card'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
   const originalIndex = findCard(id).index
@@ -88,6 +89,8 @@
       return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'table' && card.subtype === 'normaltable') {
       return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'card' && card.subtype === 'dualdatacard') {
+      return (<DoubleDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'table' && card.subtype === 'editable') {
       return (<EditTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'datacard') {
diff --git a/src/menu/components/module/voucher/index.jsx b/src/menu/components/module/voucher/index.jsx
index f1ac625..beb0972 100644
--- a/src/menu/components/module/voucher/index.jsx
+++ b/src/menu/components/module/voucher/index.jsx
@@ -39,7 +39,7 @@
         name: '鍑瘉',
         subtype: card.subtype,
         // setting: { interType: 'system' },
-        wrap: { name: '鍑瘉', title: '', width: card.width || 12, type: 'createVoucher' },
+        wrap: { name: '鍑瘉', title: '', width: card.width || 12, type: 'createVoucher', space: 30 },
         style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px', paddingBottom: '20px' },
         headerStyle: {},
         columns: [],
@@ -133,7 +133,7 @@
             <Button className="print-background header-btn">鎵撳嵃</Button>
             <Button className="out-background header-btn">鍏抽棴</Button>
           </div> : null}
-          <div className="voucher-body">
+          <div className="voucher-body" style={{padding: `0px ${card.wrap.space || 0}px`}}>
             {card.wrap.type === 'createVoucher' || card.wrap.type === 'checkVoucher' ? <div className="pre-wrap">
               <div className="voucher-code">
                 <div>璁�<DownOutlined/></div>
diff --git a/src/menu/components/module/voucher/index.scss b/src/menu/components/module/voucher/index.scss
index 98fb66f..f7b22db 100644
--- a/src/menu/components/module/voucher/index.scss
+++ b/src/menu/components/module/voucher/index.scss
@@ -7,6 +7,7 @@
   background-size: cover;
   min-height: 150px;
   overflow-y: auto;
+  overflow-x: hidden;
 
   .anticon-tool {
     position: absolute;
@@ -30,7 +31,6 @@
     }
   }
   .voucher-body {
-    padding: 0 30px;
     .voucher-code {
       display: inline-block;
       width: 160px;
diff --git a/src/menu/components/module/voucher/options.jsx b/src/menu/components/module/voucher/options.jsx
index 9867b17..bd1bee2 100644
--- a/src/menu/components/module/voucher/options.jsx
+++ b/src/menu/components/module/voucher/options.jsx
@@ -46,6 +46,7 @@
         {field: 'voucherTypeText', values: ['createVoucher', 'checkVoucher']},
         {field: 'voucherSign', values: ['createVoucher', 'checkVoucher']},
         {field: 'supModule', values: ['checkTemp', 'checkVoucher']},
+        {field: 'attachStatus', values: ['createVoucher', 'checkVoucher']},
       ]
     },
     {
@@ -113,6 +114,26 @@
       options: modules,
       allowClear: true,
     },
+    {
+      type: 'radio',
+      field: 'attachStatus',
+      label: '闄勪欢鐘舵��',
+      initval: wrap.attachStatus || 0,
+      tooltip: '娣诲姞鎴栦慨鏀瑰嚟璇佹椂锛屼笂浼犻檮浠剁殑鐘舵�併��',
+      required: true,
+      options: [
+        {value: 0, label: '寰呭鏍�'},
+        {value: 10, label: '宸插鏍�'},
+      ]
+    },
+    {
+      type: 'number',
+      field: 'space',
+      label: '鐣欑櫧',
+      initval: wrap.space || 0,
+      tooltip: '琛ㄦ牸涓讳綋閮ㄥ垎涓ょ鐨勭┖鐧借窛绂伙紝琛ㄦ牸鍦ㄧ紪杈戞椂涓ょ浼氭湁娣诲姞鍜屽垹闄ゅ浘鏍囥��',
+      required: false
+    },
   ]
 
   return wrapForm
diff --git a/src/menu/components/search/main-search/dragsearch/index.jsx b/src/menu/components/search/main-search/dragsearch/index.jsx
index 5da648f..c290b29 100644
--- a/src/menu/components/search/main-search/dragsearch/index.jsx
+++ b/src/menu/components/search/main-search/dragsearch/index.jsx
@@ -135,6 +135,10 @@
 
   let labelwidth = setting.searchLwidth !== undefined ? setting.searchLwidth : 33.3
   let advanceType = setting.advanceType || 'modal'
+  let style = {}
+  if (setting.borderRadius) {
+    style.borderRadius = setting.borderRadius
+  }
 
   return (
     <div ref={drop} className="ant-row">
@@ -161,8 +165,8 @@
         <div className="ant-row ant-form-item" style={{lineHeight: '40px', height: '55px', marginBottom: 0}}>
         <div className="ant-col ant-form-item-label" style={{width: labelwidth + '%'}}></div>
           <div className="ant-col ant-form-item-control-wrapper" style={{whiteSpace: 'nowrap'}}>
-            <Button type="primary">鎼滅储</Button>
-            <Button style={{ marginLeft: 8 }}>閲嶇疆</Button>
+            <Button style={style} type="primary">鎼滅储</Button>
+            <Button style={{ marginLeft: 8, ...style }}>閲嶇疆</Button>
             <div style={{position: 'absolute', top: 0, bottom: 0, left: 0, right: 0}}></div>
           </div>
         </div>
diff --git a/src/menu/components/search/main-search/options.jsx b/src/menu/components/search/main-search/options.jsx
index 880a5a7..f9324cf 100644
--- a/src/menu/components/search/main-search/options.jsx
+++ b/src/menu/components/search/main-search/options.jsx
@@ -117,9 +117,20 @@
       options: [
         {value: 'true', label: '鏄剧ず'},
         {value: 'false', label: '闅愯棌'},
+      ],
+      controlFields: [
+        {field: 'borderRadius', values: ['true']}
       ]
     },
     {
+      type: 'number',
+      field: 'borderRadius',
+      label: '鎸夐挳鍦嗚',
+      initval: wrap.borderRadius || '',
+      tooltip: '鎼滅储鍜岄噸缃寜閽殑鍦嗚銆�',
+      required: false
+    },
+    {
       type: 'radio',
       field: 'permission',
       label: '鏉冮檺楠岃瘉',
diff --git a/src/menu/components/share/actioncomponent/actionform/index.jsx b/src/menu/components/share/actioncomponent/actionform/index.jsx
index fd2da26..b78ff7f 100644
--- a/src/menu/components/share/actioncomponent/actionform/index.jsx
+++ b/src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -570,8 +570,8 @@
         _fieldval.label = '瀵煎嚭Excel'
         _fieldval.class = 'dgreen'
         _fieldval.execSuccess = 'never'
-        _fieldval.Ot = 'notRequired'
-        this.record.Ot = 'notRequired'
+        _fieldval.Ot = 'requiredOnce'
+        this.record.Ot = 'requiredOnce'
         this.record.label = '瀵煎嚭Excel'
         this.record.class = 'dgreen'
         this.record.execSuccess = 'never'
diff --git a/src/menu/components/share/actioncomponent/dragaction/card.jsx b/src/menu/components/share/actioncomponent/dragaction/card.jsx
index 0059c8d..3163444 100644
--- a/src/menu/components/share/actioncomponent/dragaction/card.jsx
+++ b/src/menu/components/share/actioncomponent/dragaction/card.jsx
@@ -52,7 +52,7 @@
   let _style = resetStyle(card.style)
   let _class = ''
   let show = card.show
-  if (type === 'datacard') {
+  if (type === 'datacard' || type === 'dualdatacard') {
     _style = null
     _class = 'swiper swiper-' + card.color
     show = 'button'
diff --git a/src/menu/components/share/actioncomponent/dragaction/index.jsx b/src/menu/components/share/actioncomponent/dragaction/index.jsx
index 73abf27..e797df7 100644
--- a/src/menu/components/share/actioncomponent/dragaction/index.jsx
+++ b/src/menu/components/share/actioncomponent/dragaction/index.jsx
@@ -122,12 +122,13 @@
 
         if (newcard.OpenType === 'excelIn') {
           newcard.label = item.text
-          newcard.class = item.class || 'dgreen'
+          newcard.class = 'dgreen'
           newcard.Ot = 'notRequired'
         } else if (newcard.OpenType === 'excelOut') {
           newcard.label = item.text
           newcard.execSuccess = 'never'
           newcard.class = 'dgreen'
+          newcard.Ot = 'requiredOnce'
         }
 
         if (item.overIndex) {
diff --git a/src/menu/components/share/actioncomponent/formconfig.jsx b/src/menu/components/share/actioncomponent/formconfig.jsx
index b5e6155..2a0179a 100644
--- a/src/menu/components/share/actioncomponent/formconfig.jsx
+++ b/src/menu/components/share/actioncomponent/formconfig.jsx
@@ -22,7 +22,7 @@
 
   if (card.eleType === 'button') {
     type = 'card'
-  } else if (config.subtype === 'datacard' || config.subtype === 'tablecard') {
+  } else if (config.subtype === 'datacard' || config.subtype === 'tablecard' || config.subtype === 'dualdatacard') {
     type = 'datacard'
   } else if (config.type === 'line' || config.type === 'bar' || config.type === 'scatter') {
     type = 'chart'
diff --git a/src/menu/components/share/markcomponent/index.jsx b/src/menu/components/share/markcomponent/index.jsx
index ae293fe..53a2a73 100644
--- a/src/menu/components/share/markcomponent/index.jsx
+++ b/src/menu/components/share/markcomponent/index.jsx
@@ -265,6 +265,9 @@
       if (item.signType && item.signType[0] === 'icon') {
         item.signType = [item.signType[0] + item.signType[1], item.signType[2], item.signType[3]]
       }
+      if (!item.uuid) {
+        item.uuid = Utils.getuuid()
+      }
       return item
     })
 
diff --git a/src/menu/components/share/pastecomponent/index.jsx b/src/menu/components/share/pastecomponent/index.jsx
index cb1bb7b..f8c1f6a 100644
--- a/src/menu/components/share/pastecomponent/index.jsx
+++ b/src/menu/components/share/pastecomponent/index.jsx
@@ -181,7 +181,7 @@
         })
       }
 
-      this.props.updateConfig(config)
+      this.props.updateConfig(config, type)
       this.setState({visible: false})
 
       notification.success({
diff --git a/src/menu/components/table/base-table/index.jsx b/src/menu/components/table/base-table/index.jsx
index a1d88d9..86ba414 100644
--- a/src/menu/components/table/base-table/index.jsx
+++ b/src/menu/components/table/base-table/index.jsx
@@ -385,6 +385,13 @@
     this.updateComponent(config)
   }
 
+  parseComponent = (config, type) => {
+    if (type === 'cols') {
+      this.updatecolumn(config)
+    }
+    this.updateComponent(config)
+  }
+
   render() {
     const { card } = this.state
     let options = ['action', 'search', 'form', 'cols']
@@ -397,7 +404,7 @@
               <EditOutlined style={{color: '#1890ff'}} title="缂栬緫"/>
             </NormalForm>
             <CopyComponent type="basetable" card={card}/>
-            <PasteComponent config={card} options={options} updateConfig={this.updateComponent} />
+            <PasteComponent config={card} options={options} updateConfig={this.parseComponent} />
             <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle}/>
             <SettingComponent config={card} updateConfig={this.updateComponent} />
           </div>
diff --git a/src/menu/components/tabs/tabcomponents/card.jsx b/src/menu/components/tabs/tabcomponents/card.jsx
index 9115d53..cf77eba 100644
--- a/src/menu/components/tabs/tabcomponents/card.jsx
+++ b/src/menu/components/tabs/tabcomponents/card.jsx
@@ -28,6 +28,7 @@
 const CustomChart = asyncComponent(() => import('@/menu/components/chart/chart-custom'))
 const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline'))
 const AntvG6 = asyncComponent(() => import('@/menu/components/chart/antv-G6'))
+const DoubleDataCard = asyncComponent(() => import('@/menu/components/card/double-data-card'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, unGroup, updateConfig }) => {
   const originalIndex = findCard(id).index
@@ -91,6 +92,8 @@
       return (<DataCard card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
     } else if (card.type === 'card' && card.subtype === 'propcard') {
       return (<PropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'card' && card.subtype === 'dualdatacard') {
+      return (<DoubleDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'datacard') {
       return (<CarouselDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'propcard') {
diff --git a/src/menu/components/tree/antd-tree/index.jsx b/src/menu/components/tree/antd-tree/index.jsx
index 1e69697..8e91c46 100644
--- a/src/menu/components/tree/antd-tree/index.jsx
+++ b/src/menu/components/tree/antd-tree/index.jsx
@@ -2,12 +2,13 @@
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
 import { Popover, Tree, message } from 'antd'
-import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, FileOutlined, FolderOpenOutlined } from '@ant-design/icons'
+import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, FileOutlined, FolderOpenOutlined, PlusSquareOutlined } from '@ant-design/icons'
 
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
 import { resetStyle, getTables } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
+import Utils from '@/utils/utils.js'
 import getWrapForm from './options'
 
 import './index.scss'
@@ -18,6 +19,7 @@
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
 const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
+const ActionComponent = asyncComponent(() => import('@/menu/components/share/actioncomponent'))
 
 const { TreeNode } = Tree
 
@@ -52,6 +54,7 @@
         headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
         columns: [],
         scripts: [],
+        action: [],
       }
 
       if (card.config) {
@@ -69,8 +72,11 @@
       
       this.updateComponent(_card)
     } else {
+      let _card = fromJS(card).toJS()
+      _card.action = _card.action || [] // 鍏煎
+
       this.setState({
-        card: fromJS(card).toJS()
+        card: _card
       })
     }
   }
@@ -138,6 +144,58 @@
     this.updateComponent(_card)
   }
 
+  addButton = (copy) => {
+    const { card } = this.state
+
+    let newcard = {}
+
+    if (copy) {
+      newcard = copy
+      newcard.focus = true
+    } else {
+      newcard.uuid = Utils.getuuid()
+      newcard.focus = true
+      
+      newcard.label = 'label'
+      newcard.Ot = 'requiredSgl'
+      newcard.OpenType = 'pop'
+      newcard.icon = ''
+      newcard.class = 'green'
+      newcard.intertype = card.setting.interType || 'system'
+      newcard.innerFunc = card.setting.innerFunc || ''
+      newcard.sysInterface = card.setting.sysInterface || ''
+      newcard.outerFunc = card.setting.outerFunc || ''
+      newcard.interface = card.setting.interface || ''
+      newcard.execSuccess = 'grid'
+      newcard.execError = 'never'
+      newcard.verify = null
+      newcard.show = 'button'
+      newcard.style = {marginRight: '15px'}
+    }
+
+    // 娉ㄥ唽浜嬩欢-娣诲姞鎸夐挳
+    MKEmitter.emit('addButton', card.uuid, newcard)
+  }
+
+  setSubConfig = (item) => {
+    const { card, appType } = this.state
+    let btn = fromJS(item).toJS()
+
+    if (btn.OpenType === 'pop' || btn.execMode === 'pop') {
+      if (!btn.modal) {
+        btn.modal = {
+          setting: { title: btn.label, width: appType === 'mob' ? 100 : 60, cols: '2', container: 'view', focus: '', finish: 'close', clickouter: 'unclose', display: 'modal' },
+          tables: [],
+          groups: [],
+          fields: []
+        }
+      }
+      MKEmitter.emit('changeModal', card, btn)
+    } else if (btn.OpenType === 'popview' && appType !== 'mob') {
+      MKEmitter.emit('changePopview', card, btn)
+    }
+  }
+
   getWrapForms = () => {
     const { wrap, columns } = this.state.card
 
@@ -168,8 +226,10 @@
     return (
       <div className="menu-tree-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
         <NormalHeader config={card} updateComponent={this.updateComponent}/>
+        <ActionComponent config={card} setSubConfig={this.setSubConfig} updateaction={this.updateComponent}/>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
+            <PlusSquareOutlined className="plus" title="娣诲姞鎸夐挳" onClick={() => this.addButton()}/>
             <NormalForm title="鍩烘湰璁剧疆" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
               <EditOutlined style={{color: '#1890ff'}} title="缂栬緫"/>
             </NormalForm>
diff --git a/src/menu/components/tree/antd-tree/index.scss b/src/menu/components/tree/antd-tree/index.scss
index a1255ef..20df956 100644
--- a/src/menu/components/tree/antd-tree/index.scss
+++ b/src/menu/components/tree/antd-tree/index.scss
@@ -25,6 +25,9 @@
     line-height: 90px;
     color: #bcbcbc;
   }
+  .model-menu-action-list:not(.length0) {
+    margin: 10px 0px;
+  }
 }
 .menu-tree-box::after {
   display: block;
diff --git a/src/menu/datasource/index.jsx b/src/menu/datasource/index.jsx
index 52a1544..c40ff35 100644
--- a/src/menu/datasource/index.jsx
+++ b/src/menu/datasource/index.jsx
@@ -198,6 +198,10 @@
 
       res.setting.maxScript = maxScript
 
+      if (config.subtype !== 'dualdatacard') {
+        delete res.subColumns
+      }
+
       this.setState({loading: false, visible: false})
       this.props.updateConfig({...config, ...res})
     }, () => {
diff --git a/src/menu/datasource/verifycard/index.jsx b/src/menu/datasource/verifycard/index.jsx
index 4962810..dcb2c3d 100644
--- a/src/menu/datasource/verifycard/index.jsx
+++ b/src/menu/datasource/verifycard/index.jsx
@@ -32,6 +32,7 @@
 
   state = {
     columns: [],
+    subColumns: [],
     activeKey: 'setting',
     loading: false,
     initsql: '',          // sql楠岃瘉鏃跺彉閲忓0鏄庡強璧嬪��
@@ -197,6 +198,7 @@
     this.setState({
       scripts,
       columns: config.columns ? fromJS(config.columns).toJS() : [],
+      subColumns: config.subColumns ? fromJS(config.subColumns).toJS() : [],
       setting: _setting,
       median: _setting,
       searches: search,
@@ -272,6 +274,26 @@
     values.uuid = Utils.getuuid()
 
     this.setState({ columns: [...columns, values] })
+  }
+
+  subColumnChange = (values, resolve) => {
+    const { subColumns } = this.state
+
+    let fields = subColumns.map(item => item.field.toLowerCase())
+    if (fields.includes(values.field.toLowerCase())) {
+      notification.warning({
+        top: 92,
+        message: '瀛楁宸插瓨鍦紒',
+        duration: 5
+      })
+      return
+    }
+
+    resolve()
+
+    values.uuid = Utils.getuuid()
+
+    this.setState({ subColumns: [...subColumns, values] })
   }
 
   deleteScript = (record) => {
@@ -422,6 +444,23 @@
         loading: false
       })
       this.getdefaultSql()
+    } else if (activeKey === 'subcolumns') {
+      if (this.subdatasource && this.subdatasource.state.editingKey) {
+        notification.warning({
+          top: 92,
+          message: '瀛楁鏈繚瀛橈紝璇蜂繚瀛樺悗鍒囨崲锛�',
+          duration: 5
+        })
+        this.setState({
+          loading: false
+        })
+        return
+      }
+
+      this.setState({
+        activeKey: val,
+        loading: false
+      })
     } else if (activeKey === 'scripts') {
       let _loading = false
       if (this.scriptsForm && this.scriptsForm.state.editItem) {
@@ -514,7 +553,26 @@
 
   submitDataSource = () => {
     const { config, mainSearch } = this.props
-    const { activeKey, setting, columns, scripts } = this.state
+    const { activeKey, setting, columns, subColumns, scripts } = this.state
+
+    if (config.subtype === 'dualdatacard') {
+      let arr = columns.map(col => col.field.toLowerCase())
+      let _arr = []
+      subColumns.forEach(col => {
+        if (arr.includes(col.field.toLowerCase())) {
+          _arr.push(col.field)
+        }
+      })
+
+      if (_arr.length > 0) {
+        notification.warning({
+          top: 92,
+          message: '瀛愯〃涓瓧娈�' + _arr.join('銆�') + '涓庝富琛ㄥ瓧娈甸噸澶嶏紒',
+          duration: 5
+        })
+        return Promise.reject()
+      }
+    }
 
     return new Promise((resolve, reject) => {
       if (activeKey === 'setting') {
@@ -536,7 +594,7 @@
             defaultSearch: _search,
             setting: res
           }, () => {
-            this.sqlverify(() => { resolve({setting: res, columns, scripts }) }, reject, false)
+            this.sqlverify(() => { resolve({setting: res, columns, subColumns, scripts }) }, reject, false)
           })
         }, () => {
           reject()
@@ -551,7 +609,18 @@
           reject()
           return
         }
-        this.sqlverify(() => { resolve({setting, columns, scripts }) }, reject, false)
+        this.sqlverify(() => { resolve({setting, columns, subColumns, scripts }) }, reject, false)
+      } else if (activeKey === 'subcolumns') {
+        if (this.subdatasource && this.subdatasource.state.editingKey) {
+          notification.warning({
+            top: 92,
+            message: '瀛楁鏈繚瀛橈紝璇蜂繚瀛樺悗鎻愪氦锛�',
+            duration: 5
+          })
+          reject()
+          return
+        }
+        this.sqlverify(() => { resolve({setting, columns, subColumns, scripts }) }, reject, false)
       } else if (activeKey === 'scripts') {
         let _loading = false
         if (this.scriptsForm && this.scriptsForm.state.editItem) {
@@ -570,7 +639,7 @@
           return
         }
 
-        this.sqlverify(() => { resolve({setting, columns, scripts }) }, reject, false)
+        this.sqlverify(() => { resolve({setting, columns, subColumns, scripts }) }, reject, false)
       }
     })
   }
@@ -701,18 +770,15 @@
     })
   }
 
+  updateSubfields = (columns) => {
+    this.setState({
+      subColumns: columns
+    })
+  }
+
   copyDatasource = () => {
     const { config } = this.props
-    const { columns, scripts } = this.state
-
-    if (columns.length === 0) {
-      notification.warning({
-        top: 92,
-        message: '璇锋坊鍔犲瓧娈甸泦!',
-        duration: 5
-      })
-      return
-    }
+    const { columns, subColumns, scripts } = this.state
 
     this.settingForm.handleConfirm().then(res => {
       delete res.supModule
@@ -721,7 +787,7 @@
       let source = {
         key: 'interface',
         type: 'line',
-        data: {setting: res, columns, scripts, pageable: false, format: 'array', name: res.name, status: 'false', type: 'interface', uuid: '' }
+        data: {setting: res, columns, subColumns, scripts, pageable: false, format: 'array', name: res.name, status: 'false', type: 'interface', uuid: '' }
       }
 
       try {
@@ -769,6 +835,15 @@
       }
 
       if (config.subtype !== 'basetable') {
+        let subColumns = []
+
+        if (config.subtype === 'dualdatacard' && res.data.subColumns) {
+          subColumns = res.data.subColumns.map(col => {
+            col.uuid = Utils.getuuid()
+            return col
+          })
+        }
+
         this.setState({
           scripts: res.data.scripts.map(col => {
             col.uuid = Utils.getuuid()
@@ -778,6 +853,7 @@
             col.uuid = Utils.getuuid()
             return col
           }),
+          subColumns: subColumns,
           setting: res.data.setting,
           median: res.data.setting,
           reload: true,
@@ -823,6 +899,27 @@
     message.success('澶嶅埗鎴愬姛銆�')
   }
 
+  copySubColumns = () => {
+    const { subColumns } = this.state
+    let m = []
+    let n = []
+
+    subColumns.forEach(col => {
+      m.push(`${col.field}(${col.label})`)
+      n.push(col.field)
+    })
+
+    let oInput = document.createElement('input')
+    oInput.value = `/*${m.join(',')}*/
+      ${n.join(',')}`
+    document.body.appendChild(oInput)
+    oInput.select()
+    document.execCommand('Copy')
+    document.body.removeChild(oInput)
+
+    message.success('澶嶅埗鎴愬姛銆�')
+  }
+
   /**
    * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
    */
@@ -834,7 +931,7 @@
 
   render() {
     const { config } = this.props
-    const { columns, median, setting, scripts, colColumns, scriptsColumns, activeKey, loading, searches, defaultsql, visible, pvisible, reload, script, scriptValue } = this.state
+    const { columns, subColumns, median, setting, scripts, colColumns, scriptsColumns, activeKey, loading, searches, defaultsql, visible, pvisible, reload, script, scriptValue } = this.state
 
     return (
       <div className="model-data-source-wrap">
@@ -844,13 +941,12 @@
           <TabPane tab={
             <span>
               鏁版嵁婧�
-              {config.type !== 'interface' ? <CopyOutlined title="澶嶅埗鏁版嵁婧�" className="mk-copy-datasource" onClick={this.copyDatasource}/> : null}
-              {config.type !== 'interface' ? <SnippetsOutlined title="瀵煎叆鏁版嵁婧�" className="mk-paste-datasource" onClick={() => this.setState({pvisible: true})}/> : null}
             </span>
           } key="setting">
             {!reload ? <SettingForm
               config={config}
               columns={columns}
+              subColumns={subColumns}
               setting={setting}
               scripts={scripts}
               updateStatus={(res) => this.setState({median: {...res}})}
@@ -861,21 +957,38 @@
             <span>
               瀛楁闆�
               {columns.length ? <span className="count-tip">{columns.length}</span> : null}
-              <CopyOutlined title="浠ラ�楀彿鎷兼帴褰㈠紡澶嶅埗瀛楁" className="mk-copy-fields" onClick={this.copyColumns}/>
             </span>
           } key="columns">
             <ColForm columnChange={this.columnChange}/>
             <FieldsComponent
-              config={{...config, columns}}
+              config={{columns}}
               type="fields"
               updatefield={this.updatefields}
             />
             <EditTable actions={['edit', 'move', 'copy', 'del', 'clear']} type="datasourcefield" wrappedComponentRef={(inst) => this.datasource = inst} data={columns} columns={colColumns} onChange={(columns) => this.setState({columns})}/>
           </TabPane> : null}
+          {config.subtype === 'dualdatacard' ? <TabPane tab={
+            <span>
+              瀛愯〃瀛楁闆�
+              {subColumns.length ? <span className="count-tip">{subColumns.length}</span> : null}
+            </span>
+          } key="subcolumns">
+            <ColForm columnChange={this.subColumnChange}/>
+            <FieldsComponent
+              config={{columns: subColumns}}
+              type="fields"
+              updatefield={this.updateSubfields}
+            />
+            <EditTable actions={['edit', 'move', 'copy', 'del', 'clear']} type="datasourcefield" wrappedComponentRef={(inst) => this.subdatasource = inst} data={subColumns} columns={colColumns} onChange={(subColumns) => this.setState({subColumns})}/>
+          </TabPane> : null}
           <TabPane tab={
             <span>
               鑷畾涔夎剼鏈�
               {scripts.length ? <span className="count-tip">{scripts.length}</span> : null}
+              {config.type !== 'interface' && activeKey === 'setting' ? <CopyOutlined title="澶嶅埗鏁版嵁婧�" className="mk-copy-datasource" onClick={(e) => {e.stopPropagation();this.copyDatasource()}}/> : null}
+              {config.type !== 'interface' && activeKey === 'setting' ? <SnippetsOutlined title="瀵煎叆鏁版嵁婧�" className="mk-paste-datasource" onClick={(e) => {e.stopPropagation();this.setState({pvisible: true})}}/> : null}
+              {activeKey === 'columns' ? <CopyOutlined title="浠ラ�楀彿鎷兼帴褰㈠紡澶嶅埗瀛楁" className="mk-copy-fields" onClick={(e) => {e.stopPropagation();this.copyColumns()}}/> : null}
+              {activeKey === 'subcolumns' ? <CopyOutlined title="浠ラ�楀彿鎷兼帴褰㈠紡澶嶅埗瀛楁" className="mk-copy-fields" onClick={(e) => {e.stopPropagation();this.copySubColumns()}}/> : null}
             </span>
           } key="scripts" disabled={median.interType !== 'system'} id="mk-scripts-tabpane">
             {scripts.length ? <BorderOutlined className="full-scripts" onClick={() => {
diff --git a/src/menu/datasource/verifycard/index.scss b/src/menu/datasource/verifycard/index.scss
index 7fcbe7e..b9bc0fe 100644
--- a/src/menu/datasource/verifycard/index.scss
+++ b/src/menu/datasource/verifycard/index.scss
@@ -14,9 +14,9 @@
       cursor: pointer;
       z-index: 1;
       top: 19px;
-      right: -210px;
+      right: -50px;
       color: rgb(24, 144, 255);
-      display: none;
+      display: inline-block;
       opacity: 0.4;
       transition: opacity 0.2s;
     }
@@ -25,9 +25,9 @@
       cursor: pointer;
       z-index: 1;
       top: 19px;
-      right: -319px;
+      right: -50px;
       color: rgb(24, 144, 255);
-      display: none;
+      display: inline-block;
       opacity: 0.4;
       transition: opacity 0.2s;
     }
@@ -36,19 +36,14 @@
       cursor: pointer;
       z-index: 1;
       top: 19px;
-      right: -355px;
+      right: -90px;
       color:purple;
-      display: none;
+      display: inline-block;
       opacity: 0.4;
       transition: opacity 0.2s;
     }
     .mk-copy-fields:hover, .mk-copy-datasource:hover, .mk-paste-datasource:hover {
       opacity: 1;
-    }
-    .ant-tabs-tab-active {
-      .mk-copy-fields, .mk-copy-datasource, .mk-paste-datasource {
-        display: inline-block;
-      }
     }
     .count-tip {
       position: absolute;
diff --git a/src/menu/datasource/verifycard/settingform/index.jsx b/src/menu/datasource/verifycard/settingform/index.jsx
index ad9a2bd..060d782 100644
--- a/src/menu/datasource/verifycard/settingform/index.jsx
+++ b/src/menu/datasource/verifycard/settingform/index.jsx
@@ -460,7 +460,7 @@
               </Form.Item>
             </Col> : null}
             {/* 1銆佷笉鍒嗛〉涓斾笉瀛樺湪涓婄骇妯″潡 */}
-            {!['navbar', 'interface'].includes(config.type) && !['editable', 'basetable'].includes(config.subtype) && (!config.pageable || (config.pageable && setting.laypage === 'false')) && (setting.supModule.length === 0 || setting.supModule[0] === 'empty') && setting.interType === 'system' ? <Col span={8}>
+            {!['navbar', 'interface'].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' ? <Col span={8}>
               <Form.Item label={
                 <Tooltip placement="topLeft" title={'鍒濆鍖栧姞杞芥椂锛屾槸鍚︿笌鍏朵粬缁勪欢涓�鍚屽姞杞芥暟鎹紝娉細浠呭湪浣跨敤绯荤粺鍑芥暟锛屼笖鍒濆鍖栧姞杞芥暟鎹椂鏈夋晥锛屽垎椤佃姹傛椂鏃犳晥銆�'}>
                   <QuestionCircleOutlined className="mk-form-tip" />
@@ -556,6 +556,54 @@
                 })(<InputNumber min={0} max={500} precision={0} />)}
               </Form.Item>
             </Col> : null}
+            {config.subtype === 'dualdatacard' ? <Col span={8}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="瀛愯〃鍦ㄤ富琛ㄤ腑鐨勬暟鎹泦鍚嶇О銆�">
+                  <QuestionCircleOutlined className="mk-form-tip" />
+                  瀛愯〃瀛楁
+                </Tooltip>
+              }>
+                {getFieldDecorator('subdata', {
+                  initialValue: setting.subdata || 'sub_data',
+                  rules: [
+                    {
+                      required: true,
+                      message: '璇疯緭鍏ラ粯璁ゆ帓搴�!'
+                    }
+                  ]
+                })(<Input placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+            </Col> : null}
+            {config.subtype === 'dualdatacard' ? <Col span={8}>
+              <Form.Item label="瀛愯〃涓婚敭">
+                {getFieldDecorator('subKey', {
+                  initialValue: setting.subKey || ''
+                })(
+                  <Select>
+                    {this.props.subColumns.map((option, i) =>
+                      <Select.Option key={i} value={option.field}>
+                        {option.label}
+                      </Select.Option>
+                    )}
+                  </Select>
+                )}
+              </Form.Item>
+            </Col> : null}
+            {config.subtype === 'dualdatacard' ? <Col span={8}>
+              <Form.Item label="瀛愯〃BID">
+                {getFieldDecorator('subBID', {
+                  initialValue: setting.subBID || ''
+                })(
+                  <Select>
+                    {this.props.subColumns.map((option, i) =>
+                      <Select.Option key={i} value={option.field}>
+                        {option.label}
+                      </Select.Option>
+                    )}
+                  </Select>
+                )}
+              </Form.Item>
+            </Col> : null}
           </Row>
         </Form>
       </div>
diff --git a/src/menu/menushell/card.jsx b/src/menu/menushell/card.jsx
index 5d9b196..467795d 100644
--- a/src/menu/menushell/card.jsx
+++ b/src/menu/menushell/card.jsx
@@ -11,6 +11,7 @@
 const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
 const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
 const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
+const DoubleDataCard = asyncComponent(() => import('@/menu/components/card/double-data-card'))
 const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
 const Balcony = asyncComponent(() => import('@/menu/components/card/balcony'))
 const NormalTree = asyncComponent(() => import('@/menu/components/tree/antd-tree'))
@@ -78,6 +79,8 @@
       return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'search') {
       return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'card' && card.subtype === 'dualdatacard') {
+      return (<DoubleDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'pie') {
       return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'dashboard') {
diff --git a/src/menu/modalconfig/index.jsx b/src/menu/modalconfig/index.jsx
index e205f50..7ed61f4 100644
--- a/src/menu/modalconfig/index.jsx
+++ b/src/menu/modalconfig/index.jsx
@@ -106,7 +106,7 @@
    * 3銆佽缃紪杈戝弬鏁伴」-formlist
    */
   handleForm = (_card) => {
-    const { componentConfig } = this.props
+    const { componentConfig, btn } = this.props
     const { config } = this.state
     let card = fromJS(_card).toJS()
     let _inputfields = []
@@ -119,35 +119,28 @@
     let index = null
     uniq.set(card.field, true)
 
-    let _inputIndex = 1
-    let _tabIndex = 1
-    let _linkIndex = 1
     config.fields.forEach((item, i) => {
       if (card.uuid === item.uuid) {
         index = i
       }
 
-      let label = `${item.field || ''}锛�${item.label}锛塦
-
       if (['text', 'number', 'textarea', 'color'].includes(item.type) && card.field !== item.field) {
         _inputfields.push({
           field: item.field,
-          label: _inputIndex + '銆�' + label
+          label: item.label
         })
-        _inputIndex++
       }
       if (card.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type)) {
         _tabfields.push({
           field: item.field,
-          label: _tabIndex + '銆�' + label
+          label: item.label
         })
-        _tabIndex++
       }
 
       if (item.type === 'switch') {
         _linksupFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + label
+          label: item.label
         })
       }
 
@@ -158,13 +151,12 @@
 
         _linkableFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + item.label + ' (琛ㄥ崟)'
+          label: item.label + '-琛ㄥ崟'
         })
         _linksupFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + label
+          label: item.label
         })
-        _linkIndex++
       }
     })
     if (index !== null) {
@@ -175,17 +167,19 @@
       }
     }
 
-    _tabfields.unshift({field: '', label: '鍘熻〃鍗�'})
+    let columns = componentConfig.columns
+    if (btn.$sub) {
+      columns = componentConfig.subColumns || []
+    }
 
-    componentConfig.columns.forEach(col => {
+    columns.forEach(col => {
       if (col.field && !uniq.has(col.field)) {
         uniq.set(col.field, true)
 
         _linkableFields.push({
           field: col.field,
-          label: _linkIndex + '銆�' + col.label + ' (鏄剧ず鍒�)'
+          label: col.label + '-鏄剧ず鍒�'
         })
-        _linkIndex++
       }
     })
 
@@ -206,7 +200,7 @@
       standardform,
       visible: true,
       card: card,
-      formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields, componentConfig.columns)
+      formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields, columns)
     })
   }
 
diff --git a/src/menu/modulesource/option.jsx b/src/menu/modulesource/option.jsx
index 8273421..696b3a5 100644
--- a/src/menu/modulesource/option.jsx
+++ b/src/menu/modulesource/option.jsx
@@ -1,7 +1,7 @@
 import bar from '@/assets/mobimg/bar.png'
 import bar1 from '@/assets/mobimg/bar1.png'
 import line from '@/assets/mobimg/line.png'
-import line1 from '@/assets/mobimg/line1.png'
+// import line1 from '@/assets/mobimg/line1.png'
 import tabs from '@/assets/mobimg/tabs.png'
 import group from '@/assets/mobimg/group.png'
 import card1 from '@/assets/mobimg/card1.png'
@@ -40,6 +40,7 @@
   { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '鏁版嵁鍗�', width: 24 },
   { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '灞炴�у崱', width: 24 },
   { type: 'menu', url: card2, component: 'balcony', subtype: 'balcony', title: '娴姩鍗�', width: 24},
+  { type: 'menu', url: card1, component: 'card', subtype: 'dualdatacard', title: '鍙岄噸鏁版嵁鍗�', width: 24 },
   { type: 'menu', url: simpleform, component: 'form', subtype: 'simpleform', title: '琛ㄥ崟', width: 24, forbid: ['billPrint'] },
   { type: 'menu', url: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟锛堝垎姝ワ級', width: 24, forbid: ['billPrint'] },
   { type: 'menu', url: tabForm, component: 'form', subtype: 'tabform', title: '琛ㄥ崟锛坱ab椤碉級', width: 24, forbid: ['billPrint'] },
@@ -51,7 +52,7 @@
   { type: 'menu', url: timeline, component: 'timeline', subtype: 'timeline', title: '鏃堕棿杞�', width: 12 },
   { type: 'menu', url: tree, component: 'tree', subtype: 'normaltree', title: '鏍戝舰鍒楄〃', width: 12, forbid: ['billPrint'] },
   { type: 'menu', url: line, component: 'line', subtype: 'line', title: '鎶樼嚎鍥�', width: 24 },
-  { type: 'menu', url: line1, component: 'line', subtype: 'line1', title: '闃舵鎶樼嚎鍥�', width: 24 },
+  // { type: 'menu', url: line1, component: 'line', subtype: 'line1', title: '闃舵鎶樼嚎鍥�', width: 24 },
   { type: 'menu', url: bar, component: 'bar', subtype: 'bar', title: '鏌辩姸鍥�', width: 24 },
   { type: 'menu', url: bar1, component: 'bar', subtype: 'bar1', title: '鏉″舰鍥�', width: 24 },
   { type: 'menu', url: Pie, component: 'pie', subtype: 'pie', title: '楗煎浘', width: 12 },
diff --git a/src/mob/components/tabs/tabcomponents/card.jsx b/src/mob/components/tabs/tabcomponents/card.jsx
index 76430f6..24dc025 100644
--- a/src/mob/components/tabs/tabcomponents/card.jsx
+++ b/src/mob/components/tabs/tabcomponents/card.jsx
@@ -26,6 +26,7 @@
 const Balcony = asyncComponent(() => import('@/menu/components/card/balcony'))
 const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
 const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline'))
+const DoubleDataCard = asyncComponent(() => import('@/menu/components/card/double-data-card'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, unGroup, updateConfig }) => {
   const originalIndex = findCard(id).index
@@ -89,6 +90,8 @@
       return (<DataCard card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
     } else if (card.type === 'card' && card.subtype === 'propcard') {
       return (<PropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'card' && card.subtype === 'dualdatacard') {
+      return (<DoubleDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'datacard') {
       return (<CarouselDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'propcard') {
diff --git a/src/mob/mobshell/card.jsx b/src/mob/mobshell/card.jsx
index 6dfc6c6..fdca2fd 100644
--- a/src/mob/mobshell/card.jsx
+++ b/src/mob/mobshell/card.jsx
@@ -32,6 +32,7 @@
 const OfficialAccount = asyncComponent(() => import('@/mob/components/official'))
 const ShareCode = asyncComponent(() => import('@/mob/components/sharecode'))
 const Iframe = asyncComponent(() => import('@/menu/components/iframe'))
+const DoubleDataCard = asyncComponent(() => import('@/menu/components/card/double-data-card'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, unGroup, updateConfig }) => {
   const originalIndex = findCard(id).index
@@ -111,6 +112,8 @@
       return (<DataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'card' && card.subtype === 'propcard') {
       return (<PropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'card' && card.subtype === 'dualdatacard') {
+      return (<DoubleDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'datacard') {
       return (<CarouselDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'propcard') {
diff --git a/src/mob/modalconfig/index.jsx b/src/mob/modalconfig/index.jsx
index 97d305d..570c637 100644
--- a/src/mob/modalconfig/index.jsx
+++ b/src/mob/modalconfig/index.jsx
@@ -127,7 +127,7 @@
    * 3銆佽缃紪杈戝弬鏁伴」-formlist
    */
   handleForm = (_card) => {
-    const { componentConfig } = this.props
+    const { componentConfig, btn } = this.props
     const { config } = this.state
     let card = fromJS(_card).toJS()
     let _inputfields = []
@@ -140,35 +140,28 @@
     let index = null
     uniq.set(card.field, true)
 
-    let _inputIndex = 1
-    let _tabIndex = 1
-    let _linkIndex = 1
     config.fields.forEach((item, i) => {
       if (card.uuid === item.uuid) {
         index = i
       }
 
-      let label = `${item.field || ''}锛�${item.label}锛塦
-
       if (['text', 'number', 'textarea', 'color'].includes(item.type) && card.field !== item.field) {
         _inputfields.push({
           field: item.field,
-          label: _inputIndex + '銆�' + label
+          label: item.label
         })
-        _inputIndex++
       }
       if (card.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type)) {
         _tabfields.push({
           field: item.field,
-          label: _tabIndex + '銆�' + label
+          label: item.label
         })
-        _tabIndex++
       }
 
       if (item.type === 'switch') {
         _linksupFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + label
+          label: item.label
         })
       }
 
@@ -179,17 +172,14 @@
 
         _linkableFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + item.label + ' (琛ㄥ崟)'
+          label: item.label + '-琛ㄥ崟'
         })
         _linksupFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + label
+          label: item.label
         })
-        _linkIndex++
       }
     })
-
-    _tabfields.unshift({field: '', label: '鍘熻〃鍗�'})
 
     if (index !== null) {
       if (index === 0) {
@@ -199,15 +189,19 @@
       }
     }
 
-    componentConfig.columns.forEach(col => {
+    let columns = componentConfig.columns
+    if (btn.$sub) {
+      columns = componentConfig.subColumns || []
+    }
+
+    columns.forEach(col => {
       if (col.field && !uniq.has(col.field)) {
         uniq.set(col.field, true)
 
         _linkableFields.push({
           field: col.field,
-          label: _linkIndex + '銆�' + col.label + ' (鏄剧ず鍒�)'
+          label: col.label + '-鏄剧ず鍒�'
         })
-        _linkIndex++
       }
     })
 
@@ -220,7 +214,7 @@
       standardform,
       visible: true,
       card: card,
-      formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields, componentConfig.columns)
+      formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields, columns)
     })
   }
 
diff --git a/src/mob/modulesource/option.jsx b/src/mob/modulesource/option.jsx
index 1fc604b..f4b8529 100644
--- a/src/mob/modulesource/option.jsx
+++ b/src/mob/modulesource/option.jsx
@@ -1,7 +1,7 @@
 import bar from '@/assets/mobimg/bar.png'
 import bar1 from '@/assets/mobimg/bar1.png'
 import line from '@/assets/mobimg/line.png'
-import line1 from '@/assets/mobimg/line1.png'
+// import line1 from '@/assets/mobimg/line1.png'
 import tabs from '@/assets/mobimg/tabs.png'
 import group from '@/assets/mobimg/group.png'
 import card1 from '@/assets/mobimg/card1.png'
@@ -41,6 +41,7 @@
   { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '鏁版嵁鍗�', width: 24 },
   { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '灞炴�у崱', width: 24 },
   { type: 'menu', url: card2, component: 'balcony', subtype: 'balcony', title: '娴姩鍗�', width: 24 },
+  { type: 'menu', url: card1, component: 'card', subtype: 'dualdatacard', title: '鍙岄噸鏁版嵁鍗�', width: 24 },
   { type: 'menu', url: form, component: 'form', subtype: 'simpleform', title: '琛ㄥ崟', width: 24 },
   { type: 'menu', url: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟锛堝垎姝ワ級', width: 24 },
   { type: 'menu', url: tabForm, component: 'form', subtype: 'tabform', title: '琛ㄥ崟锛坱ab椤碉級', width: 24 },
@@ -50,7 +51,7 @@
   { type: 'menu', url: NormalTable, component: 'table', subtype: 'normaltable', title: '甯哥敤琛�', width: 24 },
   { type: 'menu', url: timeline, component: 'timeline', subtype: 'timeline', title: '鏃堕棿杞�', width: 24 },
   { type: 'menu', url: line, component: 'line', subtype: 'line', title: '鎶樼嚎鍥�', width: 24 },
-  { type: 'menu', url: line1, component: 'line', subtype: 'line1', title: '闃舵鎶樼嚎鍥�', width: 24 },
+  // { type: 'menu', url: line1, component: 'line', subtype: 'line1', title: '闃舵鎶樼嚎鍥�', width: 24 },
   { type: 'menu', url: bar, component: 'bar', subtype: 'bar', title: '鏌辩姸鍥�', width: 24 },
   { type: 'menu', url: bar1, component: 'bar', subtype: 'bar1', title: '鏉″舰鍥�', width: 24 },
   { type: 'menu', url: Pie, component: 'pie', subtype: 'pie', title: '楗煎浘', width: 24 },
diff --git a/src/pc/menushell/card.jsx b/src/pc/menushell/card.jsx
index e003785..7cdc671 100644
--- a/src/pc/menushell/card.jsx
+++ b/src/pc/menushell/card.jsx
@@ -31,6 +31,7 @@
 const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline'))
 const AntvG6 = asyncComponent(() => import('@/menu/components/chart/antv-G6'))
 const Iframe = asyncComponent(() => import('@/menu/components/iframe'))
+const DoubleDataCard = asyncComponent(() => import('@/menu/components/card/double-data-card'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, unGroup, updateConfig }) => {
   const originalIndex = findCard(id).index
@@ -99,6 +100,8 @@
       return (<DataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'card' && card.subtype === 'propcard') {
       return (<PropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'card' && card.subtype === 'dualdatacard') {
+      return (<DoubleDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'datacard') {
       return (<CarouselDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'propcard') {
diff --git a/src/pc/modulesource/option.jsx b/src/pc/modulesource/option.jsx
index db77df6..e5dbf8a 100644
--- a/src/pc/modulesource/option.jsx
+++ b/src/pc/modulesource/option.jsx
@@ -1,7 +1,7 @@
 import bar from '@/assets/mobimg/bar.png'
 import bar1 from '@/assets/mobimg/bar1.png'
 import line from '@/assets/mobimg/line.png'
-import line1 from '@/assets/mobimg/line1.png'
+// import line1 from '@/assets/mobimg/line1.png'
 import tabs from '@/assets/mobimg/tabs.png'
 import group from '@/assets/mobimg/group.png'
 import card1 from '@/assets/mobimg/card1.png'
@@ -37,6 +37,7 @@
   { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '鏁版嵁鍗�', width: 24 },
   { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '灞炴�у崱', width: 24 },
   { type: 'menu', url: card2, component: 'balcony', subtype: 'balcony', title: '娴姩鍗�', width: 24 },
+  { type: 'menu', url: card1, component: 'card', subtype: 'dualdatacard', title: '鍙岄噸鏁版嵁鍗�', width: 24 },
   { type: 'menu', url: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟锛堝垎姝ワ級', width: 24 },
   { type: 'menu', url: tabForm, component: 'form', subtype: 'tabform', title: '琛ㄥ崟锛坱ab椤碉級', width: 24 },
   { type: 'menu', url: Carousel, component: 'carousel', subtype: 'datacard', title: '杞挱-鍔ㄦ�佹暟鎹�', width: 24 },
@@ -47,7 +48,7 @@
   { type: 'menu', url: timeline, component: 'timeline', subtype: 'timeline', title: '鏃堕棿杞�', width: 12 },
   { type: 'menu', url: tree, component: 'tree', subtype: 'normaltree', title: '鏍戝舰鍒楄〃', width: 12 },
   { type: 'menu', url: line, component: 'line', subtype: 'line', title: '鎶樼嚎鍥�', width: 24 },
-  { type: 'menu', url: line1, component: 'line', subtype: 'line1', title: '闃舵鎶樼嚎鍥�', width: 24 },
+  // { type: 'menu', url: line1, component: 'line', subtype: 'line1', title: '闃舵鎶樼嚎鍥�', width: 24 },
   { type: 'menu', url: bar, component: 'bar', subtype: 'bar', title: '鏌辩姸鍥�', width: 24 },
   { type: 'menu', url: bar1, component: 'bar', subtype: 'bar1', title: '鏉″舰鍥�', width: 24 },
   { type: 'menu', url: Pie, component: 'pie', subtype: 'pie', title: '楗煎浘', width: 12 },
diff --git a/src/tabviews/commontable/index.jsx b/src/tabviews/commontable/index.jsx
index f224e98..1133667 100644
--- a/src/tabviews/commontable/index.jsx
+++ b/src/tabviews/commontable/index.jsx
@@ -319,6 +319,7 @@
       config.action.forEach(item => {
         item.logLabel = MenuName + '-' + item.label // 鐢ㄤ簬sPC_TableData_InUpDe璁板綍鎿嶄綔鎸夐挳
         item.$menuId = this.props.MenuID
+        item.$old = true
         item.ContainerId = this.state.ContainerId
 
         if (item.OpenType === 'funcbutton' && item.funcType === 'print' && item.verify) { // 鎵撳嵃鏈鸿缃�
diff --git a/src/tabviews/custom/components/card/cardItem/index.jsx b/src/tabviews/custom/components/card/cardItem/index.jsx
index 6e46720..d02f496 100644
--- a/src/tabviews/custom/components/card/cardItem/index.jsx
+++ b/src/tabviews/custom/components/card/cardItem/index.jsx
@@ -41,8 +41,10 @@
   }
 
   openView = () => {
-    const { card, data, cards } = this.props
+    const { card, data, cards, onClick } = this.props
 
+    onClick && onClick()
+    
     if (!card.setting.click || data.$disabled) return
 
     if (card.setting.click === 'menus' && cards.subtype === 'datacard' && card.$cardType !== 'extendCard') {
@@ -145,8 +147,10 @@
   }
 
   doubleClick = () => {
-    const { card, data, cards } = this.props
+    const { card, data, cards, onDoubleClick } = this.props
 
+    onDoubleClick && onDoubleClick()
+    
     if (card.setting.click !== 'button' || card.setting.clickType !== 'multi' || data.$disabled) return
 
     if (card.setting.linkbtn) {
@@ -176,7 +180,7 @@
     }
 
     return (
-      <div className={'card-item-box ' + (card.setting.btnControl || '')} style={style} onClick={this.openView} onDoubleClick={this.doubleClick}>
+      <div className={'card-item-box ' + (card.setting.btnControl || '') + (card.setting.click ? ' mk-pointer' : '')} style={style} onClick={this.openView} onDoubleClick={this.doubleClick}>
         {bg}
         {children}
         <CardCellComponent data={data} cards={cards} cardCell={card} elements={card.elements}/>
diff --git a/src/tabviews/custom/components/card/cardItem/index.scss b/src/tabviews/custom/components/card/cardItem/index.scss
index 0879249..c6f2f31 100644
--- a/src/tabviews/custom/components/card/cardItem/index.scss
+++ b/src/tabviews/custom/components/card/cardItem/index.scss
@@ -49,6 +49,12 @@
     background-repeat: inherit;
   }
 }
+.card-item-box.mk-pointer {
+  cursor: pointer;
+}
+.mk-disabled .card-item-box.mk-pointer {
+  cursor: not-allowed;
+}
 .card-item-box:hover {
   .back-side.up, .back-side.down, .back-side.left, .back-side.right {
     transform: translate(0, 0);
diff --git a/src/tabviews/custom/components/card/cardcellList/index.jsx b/src/tabviews/custom/components/card/cardcellList/index.jsx
index 993d189..5a3a3e1 100644
--- a/src/tabviews/custom/components/card/cardcellList/index.jsx
+++ b/src/tabviews/custom/components/card/cardcellList/index.jsx
@@ -475,6 +475,10 @@
           icon = card.icon
         }
 
+        if (!icon && card.noValue === 'hide') { // 绌哄�奸殣钘�
+          return null
+        }
+
         if (!height) { // 鍏煎
           let fontSize = 14
           let lineHeight = 1.5
diff --git a/src/tabviews/custom/components/card/data-card/index.jsx b/src/tabviews/custom/components/card/data-card/index.jsx
index edc216a..5d917d4 100644
--- a/src/tabviews/custom/components/card/data-card/index.jsx
+++ b/src/tabviews/custom/components/card/data-card/index.jsx
@@ -99,8 +99,6 @@
 
     _config.subcards = null
     
-    let _cols = new Map()
-
     let _data = null
     let _sync = _config.setting.sync === 'true'
 
@@ -144,24 +142,6 @@
         }
       }
     }
-
-    _config.columns.forEach(item => {
-      if (item.type !== 'number') return
-      _cols.set(item.field, item)
-    })
-
-    _card.elements = _card.elements.map(item => {
-      if (item.eleType === 'number' && item.field && _cols.has(item.field) && typeof(item.decimal) !== 'number') {
-        item.decimal = _cols.get(item.field).decimal || 0
-      }
-      return item
-    })
-    _card.backElements = _card.backElements.map(item => {
-      if (item.eleType === 'number' && item.field && _cols.has(item.field) && typeof(item.decimal) !== 'number') {
-        item.decimal = _cols.get(item.field).decimal || 0
-      }
-      return item
-    })
 
     let supComs = null
     if (_config.wrap.supType === 'multi') {
@@ -937,7 +917,7 @@
     }
 
     let checkAll = ''
-    if (config.wrap.selStyle.indexOf('check') > -1) {
+    if (config.wrap.selStyle && config.wrap.selStyle.indexOf('check') > -1) {
       if (selectedData.length > 0) {
         checkAll = selectedData.length < data.length ? ' half' : ' whole'
       }
@@ -973,7 +953,7 @@
               </Col>
             ))}
             {data && data.map((item, index) => {
-              let className = card.setting.click ? 'mk-card pointer ' : 'mk-card '
+              let className = 'mk-card '
               if (config.wrap.parity === 'true') {
                 if (index % 2 === 1) {
                   className += 'mk-parity-bg '
@@ -988,8 +968,8 @@
               }
 
               return (
-                <Col className={className} key={index} span={card.setting.width} onClick={() => {this.changeCard(index, item)}}>
-                  <CardItem card={card} cards={config} data={item}>
+                <Col className={className} key={index} span={card.setting.width}>
+                  <CardItem card={card} cards={config} data={item} onClick={() => {this.changeCard(index, item)}}>
                     <span className="circle-select"></span>
                   </CardItem>
                 </Col>
@@ -1012,7 +992,5 @@
     )
   }
 }
-
-
 
 export default DataCard
\ No newline at end of file
diff --git a/src/tabviews/custom/components/card/data-card/index.scss b/src/tabviews/custom/components/card/data-card/index.scss
index 9325019..820b993 100644
--- a/src/tabviews/custom/components/card/data-card/index.scss
+++ b/src/tabviews/custom/components/card/data-card/index.scss
@@ -50,9 +50,6 @@
   }
   .card-row-list {
     flex: 10;
-    .mk-card.pointer {
-      cursor: pointer;
-    }
   }
   .card-row-list.flex-layout {
     display: flex;
@@ -200,6 +197,9 @@
     .circle-select {
       border-radius: 0!important;
     }
+    .circle-select.half::after {
+      border-radius: 0px;
+    }
   }
   .data-zoom.check {
     .mk-card.active, .mk-card.selected {
@@ -224,12 +224,13 @@
       left: 4px;
       content: ' ';
       display: block;
+      border-radius: 8px;
       width: 8px;
       height: 8px;
       background: var(--mk-sys-color);
     }
     .card-item-box {
-      width: 100%;
+      // width: 100%;
       display: flex;
     }
     .card-cell-list {
diff --git a/src/tabviews/custom/components/card/double-data-card/index.jsx b/src/tabviews/custom/components/card/double-data-card/index.jsx
new file mode 100644
index 0000000..9431a8d
--- /dev/null
+++ b/src/tabviews/custom/components/card/double-data-card/index.jsx
@@ -0,0 +1,956 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Spin, Empty, notification, Row, Col, Pagination, Modal } from 'antd'
+import { DownOutlined, UpOutlined, PlusSquareOutlined, MinusSquareOutlined } from '@ant-design/icons'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+import UtilsDM from '@/utils/utils-datamanage.js'
+import preImg from '@/assets/img/prev.png'
+import nextImg from '@/assets/img/next.png'
+import MKEmitter from '@/utils/events.js'
+import TimerTask from '@/utils/timer-task.js'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const CardItem = asyncComponent(() => import('../cardItem'))
+const MainAction = asyncComponent(() => import('@/tabviews/zshare/actionList'))
+const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
+
+class DoubleDataCard extends Component {
+  static propTpyes = {
+    config: PropTypes.object,        // 缁勪欢閰嶇疆淇℃伅
+    mainSearch: PropTypes.any,       // 澶栧眰鎼滅储鏉′欢
+  }
+
+  state = {
+    BID: '',                   // 涓婄骇ID
+    BData: '',                 // 涓婄骇琛屾暟鎹�
+    config: null,              // 鍥捐〃閰嶇疆淇℃伅
+    search: null,              // 鎼滅储鏉′欢
+    pageIndex: 1,              // 椤电爜
+    activeKey: '',             // 閫変腑鍗�
+    selectKeys: [],            // 澶氶�夋椂閫変腑鍗$墖
+    selectedData: [],          // 閫変腑鏁版嵁锛岀敤浜庡伐鍏锋爮鎸夐挳
+    loading: false,            // 鏁版嵁鍔犺浇鐘舵��
+    card: null,                // 鍗$墖璁剧疆
+    data: null,                // 鏁版嵁
+    total: null,
+    precards: [],
+    nextcards: [],
+    selected: 'false',
+    opens: [],
+    wrapStyle: null,
+    subcard: null,
+    subconfig: null
+  }
+
+  loaded = false
+
+  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 || ''
+    }
+
+    let _card = null
+    let precards = []
+    let nextcards = []
+
+    if (_config.wrap.controlField) {
+      if (_config.wrap.controlVal) {
+        _config.wrap.controlVal = _config.wrap.controlVal.split(',')
+      } else {
+        _config.wrap.controlVal = ['']
+      }
+    }
+
+    _config.subcards.forEach(item => {
+      if (item.setting.click === 'button' && !item.setting.linkbtn) {
+        item.elements.forEach(ele => {
+          if (ele.eleType === 'button') {
+            item.setting.linkbtn = ele.uuid
+          }
+        })
+        if (!item.setting.linkbtn) {
+          item.setting.click = ''
+        }
+      }
+
+      if (item.$cardType !== 'extendCard') {
+        _card = item
+      } else if (!_card) {
+        precards.push(item)
+      } else {
+        nextcards.push(item)
+      }
+    })
+
+    _config.subcards = null
+    
+    let _data = null
+
+    let selected = 'false'
+    if (_config.wrap.selected === 'always' || _config.wrap.selected === 'init' || _config.wrap.selected === 'sign') {
+      selected = _config.wrap.selected
+    } else {
+      _config.wrap.selected = 'false'
+    }
+
+    _config.wrap.selStyle = _config.wrap.selStyle || 'active'
+    _config.wrap.pagestyle = _config.wrap.pagestyle || 'page'
+
+    _config.wrap.wrapClass =  `${_config.wrap.selStyle} ${_config.wrap.cardType || ''}`
+
+    this.loaded = _data !== null
+
+    let wrapStyle = null
+    let subcard = fromJS(_card).toJS()
+    let subconfig = fromJS(_config).toJS()
+
+    subconfig.columns = subconfig.subColumns || []
+
+    subcard.style = subcard.backStyle
+    subcard.elements = subcard.backElements
+    subcard.setting = subcard.backSetting
+
+    if (_card.setting.position === 'inner') {
+      wrapStyle = {}
+      Object.keys(_card.style).forEach(key => {
+        if (!/^(margin|border|box)/.test(key)) return
+        wrapStyle[key] = _card.style[key]
+        delete _card.style[key]
+      })
+    }
+
+    _config.setting.sub_field = subconfig.columns.map(col => col.field).join(',')
+
+    this.setState({
+      selected,
+      precards,
+      nextcards,
+      data: _data,
+      BID: BID || '',
+      BData: BData || '',
+      config: _config,
+      subcard: subcard,
+      subconfig: subconfig,
+      wrapStyle: wrapStyle,
+      card: _card,
+      search: Utils.initMainSearch(_config.search),
+      arr_field: _config.columns.map(col => col.field).join(','),
+    }, () => {
+      if (_config.setting.onload === 'true') {
+        setTimeout(() => {
+          this.loadData()
+        }, _config.setting.delay || 0)
+      }
+    })
+  }
+
+  componentDidMount () {
+    const { config } = this.state
+
+    MKEmitter.addListener('reloadData', this.reloadData)
+    MKEmitter.addListener('resetSelectLine', this.resetParentParam)
+    MKEmitter.addListener('queryModuleParam', this.queryModuleParam)
+    MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult)
+    
+    if (config.timer) {
+      this.timer = new TimerTask()
+      this.timer.init(config.uuid, config.timer, config.timerRepeats, () => {
+        this.setState({
+          pageIndex: 1
+        }, () => {
+          this.loadData('', 'timer')
+        })
+      })
+    }
+
+    if (config.$cache && !this.loaded) {
+      Api.getLCacheConfig(config.uuid).then(res => {
+        if (!res || this.loaded) return
+        let _data = res.map((item, index) => {
+          if (item[config.setting.subdata]) {
+            let _children = item[config.setting.subdata]
+
+            delete item[config.setting.subdata]
+
+            item.children = _children.map((cell, i) => {
+              cell.key = i
+              cell.$$uuid = cell[config.setting.subKey] || ''
+              cell.$$BID = item[config.setting.primaryKey] || ''
+              cell.$$BData = {...item}
+              cell.$Index = i + 1 + ''
+
+              return cell
+            })
+          } else {
+            item.children = []
+          }
+
+          item.key = index
+          item.$$uuid = item[config.setting.primaryKey] || ''
+          item.$Index = index + 1 + ''
+
+          if (config.wrap.controlField) {
+            if (config.wrap.controlVal.includes(item[config.wrap.controlField])) {
+              item.$disabled = true
+            }
+          }
+
+          return item
+        })
+
+        this.setState({data: _data})
+      })
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    const { config } = this.state
+
+    if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+      this.setState({pageIndex: 1}, () => {
+        this.loadData()
+      })
+    }
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('reloadData', this.reloadData)
+    MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
+    MKEmitter.removeListener('queryModuleParam', this.queryModuleParam)
+    MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
+
+    this.timer && this.timer.stop()
+  }
+
+  /**
+   * @description 鎸夐挳鎵ц瀹屾垚鍚庨〉闈㈠埛鏂�
+   * @param {*} menuId     // 鑿滃崟Id
+   * @param {*} position   // 鍒锋柊浣嶇疆
+   * @param {*} btn        // 鎵ц鐨勬寜閽�
+   */
+  refreshByButtonResult = (menuId, position, btn, id = '', lines) => {
+    const { config, BID } = this.state
+
+    if (config.uuid !== menuId) return
+
+    let supModule = config.setting.supModule
+
+    if (position === 'line') {
+      if (lines && lines.length === 1) {
+        this.loadLinedata(lines[0].$$uuid)
+      } else {
+        this.loadData(id)
+      }
+    } else if ((position === 'mainline' || position === 'popclose') && supModule && BID) { // 鍒锋柊婧愮粍浠舵椂锛岄檮甯﹀埛鏂颁笂绾ц涓庡綋鍓嶇粍浠�
+      MKEmitter.emit('reloadData', supModule, BID)
+    } else if (!btn || btn.resetPageIndex !== 'false') {
+      this.setState({
+        pageIndex: 1
+      }, () => {
+        this.loadData(id)
+      })
+    } else {
+      this.loadData(id)
+    }
+
+    if (position === 'popclose') { // 鎵ц鍚姩寮圭獥鐨勬寜閽墍閫夋嫨鐨勫埛鏂伴」
+      btn.$tabId && MKEmitter.emit('refreshPopButton', btn.$tabId)
+    }
+  }
+
+  checkTopLine = (id) => {
+    const { config, data, selected } = this.state
+
+    if (!data || data.length === 0) {
+      this.setState({
+        activeKey: '',
+        selectKeys: [],
+        selectedData: []
+      })
+  
+      MKEmitter.emit('resetSelectLine', config.uuid, '', '')
+      return
+    }
+
+    if (selected === 'sign') {
+      let index = ''
+      let keys = []
+      let items = []
+      let last = ''
+      data.forEach((item, i) => {
+        if (!item.$disabled && item.selected === 'true') {
+          items.push(item)
+          keys.push(i)
+          index = i
+          last = item
+        }
+      })
+
+      this.setState({
+        activeKey: index,
+        selectKeys: keys,
+        selectedData: items
+      })
+  
+      MKEmitter.emit('resetSelectLine', config.uuid, last ? last.$$uuid : '', last)
+      return
+    }
+
+    let index = 0
+    if (id) {
+      index = data.findIndex(item => item.$$uuid === id)
+      if (index === -1) {
+        index = 0
+      }
+    }
+
+    if (data[index].$disabled) {
+      this.setState({
+        activeKey: '',
+        selectKeys: [],
+        selectedData: []
+      })
+  
+      MKEmitter.emit('resetSelectLine', config.uuid, '', '')
+      return
+    }
+
+    this.setState({
+      activeKey: index,
+      selectKeys: [index],
+      selectedData: [data[index]]
+    })
+
+    MKEmitter.emit('resetSelectLine', config.uuid, data[index].$$uuid, data[index])
+  }
+
+  checkAll = () => {
+    const { config, data, selectedData } = this.state
+
+    if (!data || data.length === 0) return
+    
+    if (selectedData.length === 0 || selectedData.length < data.length) {
+      let index = ''
+      let keys = []
+      let items = []
+      let last = ''
+
+      data.forEach((item, i) => {
+        if (item.$disabled) return
+
+        items.push(item)
+        keys.push(i)
+        index = i
+        last = item
+      })
+
+      this.setState({
+        activeKey: index,
+        selectKeys: keys,
+        selectedData: items
+      })
+  
+      MKEmitter.emit('resetSelectLine', config.uuid, last ? last.$$uuid : '', last)
+    } else {
+      this.setState({
+        activeKey: '',
+        selectKeys: [],
+        selectedData: []
+      })
+  
+      MKEmitter.emit('resetSelectLine', config.uuid, '', '')
+    }
+  }
+
+  reloadData = (menuId, id) => {
+    const { config } = this.state
+
+    if (config.uuid !== menuId) return
+
+    if (!id) {
+      this.loadData()
+    } else {
+      this.loadLinedata(id)
+    }
+  }
+
+  resetParentParam = (MenuID, id, data) => {
+    const { config } = this.state
+
+    if (!config.setting.supModule || config.setting.supModule !== MenuID) return
+    if (id !== this.state.BID || id !== '') {
+      this.setState({ BID: id, BData: data, pageIndex: 1 }, () => {
+        this.loadData()
+      })
+    }
+  }
+
+  /**
+   * @description 瀵煎嚭Excel鏃讹紝鑾峰彇椤甸潰鎼滅储鎺掑簭绛夊弬鏁�
+   */
+  queryModuleParam = (menuId, callback) => {
+    const { mainSearch } = this.props
+    const { arr_field, config, search } = this.state
+
+    if (config.uuid !== menuId) return
+
+    let searches = search ? fromJS(search).toJS() : []
+    if (config.setting.useMSearch && mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      let keys = searches.map(item => item.key.toLowerCase())
+      mainSearch.forEach(item => {
+        if (!keys.includes(item.key.toLowerCase())) {
+          searches.push(item)
+        }
+      })
+    }
+
+    callback({
+      arr_field: arr_field,
+      orderBy: config.setting.order || '',
+      search: searches,
+      menuName: config.name
+    })
+  }
+
+  async loadData (id, type) {
+    const { mainSearch } = this.props
+    const { config, arr_field, pageIndex, search, BID, BData, selected, card } = this.state
+
+    if (config.setting.supModule && !BID && config.wrap.supKey !== 'false') { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
+      this.loaded = true
+
+      this.setState({
+        activeKey: '',
+        selectKeys: [],
+        selectedData: [],
+        pageIndex: 1,
+        data: [],
+        opens: [],
+        total: 0,
+        loading: false
+      })
+      
+      if (selected !== 'false' || (id && config.wrap.selected !== 'false')) {
+        setTimeout(() => {
+          this.checkTopLine(id)
+        }, 200)
+        if (selected === 'init') {
+          this.setState({selected: 'false'})
+        }
+      } else {
+        MKEmitter.emit('resetSelectLine', config.uuid, '', '')
+      }
+      return
+    }
+
+    let searches = fromJS(search).toJS()
+    if (config.setting.useMSearch && mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      let keys = searches.map(item => item.key.toLowerCase())
+      mainSearch.forEach(item => {
+        if (!keys.includes(item.key.toLowerCase())) {
+          searches.push(item)
+        }
+      })
+    }
+
+    let requireFields = searches.filter(item => item.required && item.value === '')
+    if (requireFields.length > 0) {
+      return
+    }
+
+    if (type !== 'timer') {
+      this.setState({
+        loading: true
+      })
+    }
+
+    let _orderBy = config.setting.order || ''
+    let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, pageIndex, config.setting.pageSize, BID)
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      let start = 1
+      if (config.setting.laypage) {
+        start = config.setting.pageSize * (pageIndex - 1) + 1
+      }
+
+      this.loaded = true
+      if (config.$cache && pageIndex === 1) {
+        Api.writeCacheConfig(config.uuid, result.data || '')
+      }
+
+      if (selected !== 'false' || (id && config.wrap.selected !== 'false')) {
+        setTimeout(() => {
+          this.checkTopLine(id)
+        }, 200)
+        if (selected === 'init') {
+          this.setState({selected: 'false'})
+        }
+      } else {
+        MKEmitter.emit('resetSelectLine', config.uuid, '', '')
+      }
+
+      let data = []
+      let opens = []
+
+      if (type === 'plus') {
+        let _data = (this.state.data || []).concat(result.data || [])
+        data = _data.map((item, index) => {
+          if (item[config.setting.subdata]) {
+            let _children = item[config.setting.subdata]
+
+            delete item[config.setting.subdata]
+
+            item.children = _children.map((cell, i) => {
+              cell.key = i
+              cell.$$uuid = cell[config.setting.subKey] || ''
+              cell.$$BID = item[config.setting.primaryKey] || ''
+              cell.$$BData = {...item}
+              cell.$Index = i + 1 + ''
+
+              return cell
+            })
+          } else {
+            item.children = []
+          }
+
+          item.key = index
+          item.$$uuid = item[config.setting.primaryKey] || ''
+          item.$$BID = BID || ''
+          item.$$BData = BData || ''
+          item.$Index = index + 1 + ''
+
+          if (config.wrap.controlField) {
+            if (config.wrap.controlVal.includes(item[config.wrap.controlField])) {
+              item.$disabled = true
+            }
+          }
+          
+          return item
+        })
+      } else {
+        data = result.data.map((item, index) => {
+          if (item[config.setting.subdata]) {
+            let _children = item[config.setting.subdata]
+
+            delete item[config.setting.subdata]
+
+            item.children = _children.map((cell, i) => {
+              cell.key = i
+              cell.$$uuid = cell[config.setting.subKey] || ''
+              cell.$$BID = item[config.setting.primaryKey] || ''
+              cell.$$BData = {...item}
+              cell.$Index = i + 1 + ''
+
+              return cell
+            })
+          } else {
+            item.children = []
+          }
+
+          item.key = index
+          item.$$uuid = item[config.setting.primaryKey] || ''
+          item.$$BID = BID || ''
+          item.$$BData = BData || ''
+          item.$Index = index + start + ''
+
+          if (config.wrap.controlField) {
+            if (config.wrap.controlVal.includes(item[config.wrap.controlField])) {
+              item.$disabled = true
+            }
+          }
+          return item
+        })
+      }
+
+      if (card.setting.display === 'unfold') {
+        opens = data.map(item => item.key)
+      } else {
+        opens = []
+      }
+
+      this.setState({
+        activeKey: '',
+        selectKeys: [],
+        opens: opens,
+        selectedData: [],
+        data: data,
+        total: result.total,
+        loading: false
+      })
+    } else {
+      this.setState({
+        loading: false
+      })
+      this.timer && this.timer.stop()
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
+    }
+  }
+
+  /**
+   * @description 鑾峰彇鍗曡鏁版嵁
+   */ 
+  async loadLinedata (id) {
+    const { mainSearch } = this.props
+    const { config, arr_field, pageIndex, search, BID, BData } = this.state
+
+    let searches = fromJS(search).toJS()
+    if (config.setting.useMSearch && mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      let keys = searches.map(item => item.key.toLowerCase())
+      mainSearch.forEach(item => {
+        if (!keys.includes(item.key.toLowerCase())) {
+          searches.push(item)
+        }
+      })
+    }
+
+    this.setState({
+      loading: true
+    })
+
+    let _orderBy = config.setting.order || ''
+    let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, pageIndex, config.setting.pageSize, BID, id)
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      let data = fromJS(this.state.data).toJS()
+      if (result.data && result.data[0]) {
+        let _data = result.data[0]
+
+        try {
+          data = data.map(item => {
+            if (item[config.setting.primaryKey] === _data[config.setting.primaryKey]) {
+              if (_data[config.setting.subdata]) {
+                let _children = _data[config.setting.subdata]
+
+                delete _data[config.setting.subdata]
+
+                _data.children = _children.map((cell, i) => {
+                  cell.key = i
+                  cell.$$uuid = cell[config.setting.subKey] || ''
+                  cell.$$BID = _data[config.setting.primaryKey] || ''
+                  cell.$$BData = {..._data}
+                  cell.$Index = i + 1 + ''
+
+                  return cell
+                })
+              } else {
+                _data.children = []
+              }
+
+              _data.key = item.key
+              _data.$$uuid = _data[config.setting.primaryKey] || ''
+              _data.$$BID = BID || ''
+              _data.$$BData = BData || ''
+              _data.$Index = item.$Index
+              return _data
+            } else {
+              return item
+            }
+          })
+        } catch (e) {
+          console.warn('鏁版嵁鏌ヨ閿欒')
+        }
+
+        MKEmitter.emit('resetSelectLine', config.uuid, _data.$$uuid || '', _data)
+      }
+
+      this.setState({
+        data: data,
+        loading: false
+      })
+    } else {
+      this.setState({
+        loading: false
+      })
+      notification.error({
+        top: 92,
+        message: result.message,
+        duration: 10
+      })
+    }
+  }
+
+  loadMore = () => {
+    const { total, pageIndex, loading, config } = this.state
+
+    if (loading || config.setting.pageSize * pageIndex >= total) {
+      return
+    }
+
+    this.setState({
+      pageIndex: pageIndex + 1
+    }, () => {
+      this.loadData('', 'plus')
+    })
+  }
+
+  prevPage = () => {
+    const { pageIndex } = this.state
+
+    if (pageIndex === 1) return
+
+    this.setState({
+      pageIndex: pageIndex - 1
+    }, () => {
+      this.loadData()
+    })
+  }
+
+  nextPage = () => {
+    const { config, pageIndex, total } = this.state
+    let _total = config.setting.pageSize * pageIndex
+
+    if (_total >= total) return
+
+    this.setState({
+      pageIndex: pageIndex + 1
+    }, () => {
+      this.loadData()
+    })
+  }
+
+  changePageIndex = (page) => {
+    this.setState({
+      pageIndex: page
+    }, () => {
+      this.loadData()
+    })
+  }
+
+  refreshSearch = (list) => {
+    this.setState({
+      search: list,
+      pageIndex: 1
+    }, () => {
+      this.loadData()
+    })
+  }
+  
+  changeCard = (index, item, subClass) => {
+    const { config, selectKeys, selectedData, activeKey, opens, card } = this.state
+
+    if (card.setting.click === 'unfold' && card.setting.clickType !== 'multi') {
+      if (subClass === 'mk-unfold') {
+        this.setState({opens: opens.filter(item => item !== index)})
+      } else {
+        this.setState({opens: [...opens, index]})
+      }
+    }
+
+    if (!config.wrap.cardType) return
+    if (item.$disabled) return
+    
+    let _selectKeys = []
+    let _selectedData = []
+    let _activeKey = ''
+    let _item = item
+    if (config.wrap.cardType === 'checkbox') {
+      if (activeKey === index) {
+        _selectKeys = selectKeys.filter(key => key !== index)
+        _selectedData = selectedData.filter(cell => cell.key !== index)
+        _activeKey = _selectKeys.slice(-1)[0]
+        _item = _selectedData.slice(-1)[0] || ''
+      } else if (selectKeys.indexOf(index) > -1) {
+        _selectKeys = selectKeys.filter(key => key !== index)
+        _selectedData = selectedData.filter(cell => cell.key !== index)
+        _activeKey = activeKey
+        _item = _selectedData.filter(cell => cell.key === activeKey)[0] || ''
+      } else {
+        _selectKeys = [...selectKeys, index]
+        _selectedData = [...selectedData, item]
+        _activeKey = index
+      }
+    } else {
+      if (activeKey === index) return
+
+      _selectedData = [item]
+      _activeKey = index
+    }
+
+    this.setState({
+      activeKey: _activeKey,
+      selectKeys: _selectKeys,
+      selectedData: _selectedData
+    })
+
+    MKEmitter.emit('resetSelectLine', config.uuid, (_item ? _item.$$uuid : ''), _item)
+  }
+
+  changeUnfold = (e, i, subClass) => {
+    const { opens } = this.state
+
+    e.stopPropagation()
+
+    if (subClass === 'mk-disabled') return
+
+    if (subClass === 'mk-unfold') {
+      this.setState({opens: opens.filter(item => item !== i)})
+    } else {
+      this.setState({opens: [...opens, i]})
+    }
+  }
+
+  onDoubleClick = (i, subClass) => {
+    const { opens, card } = this.state
+
+    if (card.setting.click !== 'unfold' || card.setting.clickType !== 'multi') return
+
+    if (subClass === 'mk-unfold') {
+      this.setState({opens: opens.filter(item => item !== i)})
+    } else {
+      this.setState({opens: [...opens, i]})
+    }
+  }
+
+  render() {
+    const { config, precards, nextcards, loading, data, pageIndex, total, card, activeKey, BID, BData, selectedData, selectKeys, subcard, subconfig, wrapStyle, opens } = this.state
+
+    if (config.wrap.empty === 'hidden' && (!data || data.length === 0)) return null
+
+    let _total = 0
+    let switchable = false
+    if (config.wrap.pagestyle === 'switch' && config.pageable && config.setting.laypage && total > config.setting.pageSize && data) {
+      _total = config.setting.pageSize * pageIndex
+      switchable = true
+    }
+
+    let extendData = {$$BID: BID, $$BData: BData, $$selectedData: selectedData, $$type: 'extendCard'}
+
+    if (data && data[0]) {
+      extendData = {...extendData, ...data[0]}
+    }
+
+    let checkAll = ''
+    let mainBox = ''
+    if (config.wrap.selStyle && config.wrap.selStyle.indexOf('check') > -1) {
+      if (selectedData.length > 0) {
+        checkAll = selectedData.length < data.length ? ' half' : ' whole'
+      }
+      if (card.setting.controlIcon === 'left') {
+        checkAll += ' mk-extend-icon'
+      }
+    } else if (card.setting.display !== 'default' && card.setting.controlIcon === 'left') {
+      mainBox = 'flex-card '
+    }
+
+    return (
+      <div className="double-data-card-box" id={'anchor' + config.uuid} style={config.style}>
+        {loading ?
+          <div className="loading-mask">
+            {data ? <div className="ant-spin-blur"></div> : null}
+            <Spin />
+          </div> : null
+        }
+        <NormalHeader config={config} BID={BID} refresh={this.refreshSearch} />
+        {config.action && config.action.length > 0 ?
+          <MainAction
+            BID={BID}
+            BData={BData}
+            setting={config.setting}
+            actions={config.action}
+            columns={config.columns}
+            selectedData={selectedData}
+          /> : null
+        }
+        <div className={`data-zoom ${config.wrap.wrapClass}`}>
+          {switchable ? <div className={'prev-page ' + (pageIndex === 1 ? 'disabled' : '')} onClick={this.prevPage}><div><div><img src={preImg} alt=""/></div></div></div> : null}
+          <Row className={'card-row-list '}>
+            {precards.map((item, index) => (
+              <Col key={'pre' + index} className="extend-card" span={item.setting.width || 6}>
+                <CardItem card={item} cards={config} data={extendData}>
+                  {item.setting.checkAll === 'show' ? <span onClick={this.checkAll} className={'circle-select' + checkAll}></span> : null}
+                </CardItem>
+              </Col>
+            ))}
+            {data && data.map((item, index) => {
+              let className = 'card-item-wrap mk-card ' + mainBox
+              let subClass = 'mk-unfold'
+              let unfold = true
+
+              if (item.$disabled) {
+                className = 'mk-disabled ' + mainBox
+              } else if (activeKey === index) {
+                className += 'active'
+              } else if (selectKeys.indexOf(index) > -1) {
+                className += 'selected'
+              }
+
+              if (card.setting.display !== 'default') {
+                if (item.children.length === 0) {
+                  subClass = 'mk-disabled'
+                  unfold = false
+                } else {
+                  subClass = opens.indexOf(index) > -1 ? 'mk-unfold' : 'mk-collapse'
+                  unfold = opens.indexOf(index) > -1
+                }
+              }
+
+              return (
+                <Col key={index} span={card.setting.width}>
+                  <div className={className} style={wrapStyle}>
+                    <CardItem card={card} cards={config} data={item} onDoubleClick={() => this.onDoubleClick(index, subClass)} onClick={() => {this.changeCard(index, item, subClass)}}>
+                      <span className="circle-select"></span>
+                      {card.setting.controlIcon === 'left' ? (!unfold ? <PlusSquareOutlined className={subClass} onClick={(e) => this.changeUnfold(e, index, subClass)}/> : <MinusSquareOutlined className={subClass} onClick={(e) => this.changeUnfold(e, index, subClass)}/>) : null}
+                      {card.setting.controlIcon === 'right' ? <UpOutlined className={subClass} onClick={(e) => this.changeUnfold(e, index, subClass)}/> : null}
+                    </CardItem>
+                    <div className={'sub-card-wrap ' + subClass + (config.wrap.parity === 'true' ? ' mk-parity-bg' : '')}>
+                      {item.children.map((cell, index) => <Col key={'sub' + index} span={subcard.setting.width || 24}>
+                        <CardItem card={subcard} cards={subconfig} data={cell} />
+                      </Col>)}
+                    </div>
+                  </div>
+                </Col>
+              )
+            })}
+            {nextcards.map((item, index) => (
+              <Col key={'next' + index} className="extend-card" span={item.setting.width || 6}>
+                <CardItem card={item} cards={config} data={extendData}>
+                  {item.setting.checkAll === 'show' ? <span onClick={this.checkAll} className={'circle-select' + checkAll}></span> : null}
+                </CardItem>
+              </Col>
+            ))}
+          </Row>
+          {switchable ? <div className={'prev-page ' + (total <= _total ? 'disabled' : '')} onClick={this.nextPage}><div><div><img src={nextImg} alt=""/></div></div></div> : null}
+          {precards.length === 0 && nextcards.length === 0 && (!data || data.length === 0) ? <Empty description={false}/> : null}
+        </div>
+        {config.wrap.pagestyle === 'page' && config.setting.laypage && data ? <Pagination size="small" total={total} showTotal={t => `鍏� ${t} 鏉} pageSize={config.setting.pageSize} onChange={this.changePageIndex} current={pageIndex}/> : null}
+        {config.wrap.pagestyle === 'more' && config.setting.laypage && data && data.length > 0 ? <div className={'mk-more' + (config.setting.pageSize * pageIndex >= total ? ' disabled' : '')} onClick={this.loadMore}>鏌ョ湅鏇村<DownOutlined/></div> : null}
+      </div>
+    )
+  }
+}
+
+export default DoubleDataCard
\ No newline at end of file
diff --git a/src/tabviews/custom/components/card/double-data-card/index.scss b/src/tabviews/custom/components/card/double-data-card/index.scss
new file mode 100644
index 0000000..4969ee5
--- /dev/null
+++ b/src/tabviews/custom/components/card/double-data-card/index.scss
@@ -0,0 +1,327 @@
+.double-data-card-box {
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  min-height: 20px;
+  overflow-y: auto;
+
+  >.button-list.toolbar-button {
+    padding: 0;
+    line-height: 45px;
+    button {
+      margin-right: 0px;
+      margin-bottom: 0px;
+      min-height: 28px;
+      height: auto;
+    }
+  }
+
+  .data-zoom {
+    display: flex;
+    position: relative;
+    .mk-disabled {
+      >.card-item-box {
+        cursor: not-allowed;
+        color: #bcbcbc;
+        .ant-mk-text, .anticon {
+          color: #bcbcbc!important;
+          span {
+            color: #bcbcbc!important;
+          }
+        }
+      }
+    }
+  }
+  .data-zoom.scale {
+    .card-row-list {
+      .mk-card:hover {
+        >.card-item-box {
+          z-index: 1;
+          transform: scale(1.05);
+        }
+      }
+    }
+  }
+  .card-row-list {
+    flex: 10;
+  }
+  .card-row-list.flex-layout {
+    display: flex;
+    width: 100%;
+    >.ant-col {
+      width: 5%;
+      flex: 1;
+    }
+  }
+  .card-row-list.float-center {
+    text-align: center;
+    >.ant-col {
+      display: inline-block;
+      float: none;
+      text-align: left;
+      vertical-align: top;
+    }
+  }
+  .card-row-list.float-right {
+    text-align: right;
+    >.ant-col {
+      display: inline-block;
+      float: none;
+      text-align: left;
+      vertical-align: top;
+    }
+  }
+  .card-item-box {
+    position: relative;
+    background-color: #ffffff;
+    transition: all 0.3s;
+    background-position: center center;
+    background-repeat: no-repeat;
+    background-size: cover;
+  }
+  .prev-page {
+    width: 20px;
+    div {
+      height: 100%;
+      display: table;
+      div {
+        display: table-cell;
+        vertical-align: middle;
+      }
+    }
+    img {
+      width: 15px;
+      padding: 0 2px;
+      height: 70px;
+      cursor: pointer;
+    }
+  }
+  .prev-page.disabled {
+    img {
+      cursor: not-allowed;
+      opacity: 0.4;
+    }
+  }
+  .card-row-list::after {
+    content: ' ';
+    display: block;
+    clear: both;
+  }
+
+  .ant-empty {
+    width: 100%;
+    min-height: 100px;
+    padding-top: 15px;
+
+    .ant-empty-image {
+      height: 60px;
+    }
+  }
+  .loading-mask {
+    position: absolute;
+    left: 0px;
+    top: 0;
+    right: 0px;
+    bottom: 0px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    text-align: justify;
+    z-index: 1;
+
+    .ant-spin-blur {
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      opacity: 0.5;
+      background: #ffffff;
+    }
+  }
+  .ant-pagination {
+    padding: 10px;
+    text-align: right;
+  }
+  .mk-more {
+    text-align: center;
+    line-height: 40px;
+    cursor: pointer;
+    .anticon-down {
+      margin-left: 2px;
+    }
+  }
+  .mk-more.disabled {
+    cursor: not-allowed;
+    color: #bcbcbc;
+  }
+  .mk-disabled {
+    .circle-select {
+      border-color: #e8e8e8!important;
+      cursor: not-allowed;
+    }
+  }
+  .circle-select {
+    position: relative;
+    display: none;
+    width: 16px;
+    height: 16px;
+    border: 1px solid #cccccc;
+    border-radius: 50%;
+    box-sizing: content-box;
+    margin: auto;
+    margin-right: 5px;
+    background-color: #ffffff;
+    transition: border-color 0.2s;
+    cursor: pointer;
+  }
+  .circle-select::before {
+    position: relative;
+    top: 1px;
+    left: 6px;
+    content: ' ';
+    display: block;
+    width: 5px;
+    height: 11px;
+    border-style: solid;
+    border-width: 0 2px 2px 0;
+    border-color: #ffffff;
+    transform: rotate(45deg);
+  }
+
+  .data-zoom.check.square {
+    .circle-select {
+      border-radius: 0!important;
+    }
+    .circle-select.half::after {
+      border-radius: 0px;
+    }
+  }
+  .data-zoom.check {
+    .mk-card.active, .mk-card.selected {
+      .circle-select {
+        border-color: var(--mk-sys-color);
+        background: var(--mk-sys-color);
+      }
+    }
+    .circle-select.whole {
+      border-color: var(--mk-sys-color);
+      background: var(--mk-sys-color);
+    }
+    .circle-select.half {
+      border-color: var(--mk-sys-color);
+    }
+    .circle-select.half::before {
+      display: none;
+    }
+    .circle-select.half::after {
+      position: absolute;
+      top: 4px;
+      left: 4px;
+      content: ' ';
+      display: block;
+      border-radius: 8px;
+      width: 8px;
+      height: 8px;
+      background: var(--mk-sys-color);
+    }
+    .card-item-box {
+      display: flex;
+    }
+    .card-cell-list {
+      flex: 1;
+    }
+    .circle-select {
+      display: block;
+    }
+    .circle-select:hover {
+      border-color: var(--mk-sys-color);
+    }
+  }
+  .card-item-wrap {
+    .card-item-box {
+      >.anticon-up {
+        position: absolute;
+        right: 0px;
+        bottom: 0px;
+        padding: 10px;
+        transition: all 0.3s;
+        cursor: pointer;
+        z-index: 1;
+      }
+      >.anticon-up:not(.mk-disabled):hover, >.anticon-plus-square:not(.mk-disabled):hover, >.anticon-minus-square:not(.mk-disabled):hover {
+        color: var(--mk-sys-color);
+      }
+      >.anticon-up.mk-disabled {
+        color: #e8e8e8;
+        cursor: not-allowed;
+        transform: rotate(180deg);
+      }
+      >.anticon-up.mk-collapse {
+        transform: rotate(180deg);
+      }
+      >.anticon-plus-square {
+        margin: auto;
+        width: 30px;
+        font-size: 18px;
+      }
+      >.anticon-plus-square.mk-disabled {
+        color: #e8e8e8;
+        cursor: not-allowed;
+      }
+      >.anticon-minus-square {
+        margin: auto;
+        width: 30px;
+        font-size: 18px;
+      }
+    }
+    .sub-card-wrap::after {
+      content: ' ';
+      display: block;
+      clear: both;
+    }
+    .sub-card-wrap.mk-collapse {
+      height: 0;
+      overflow: hidden;
+      transition: height 0.3s;
+    }
+    .sub-card-wrap {
+      .card-item-box:hover {
+        background-color: var(--mk-sys-color2);
+      }
+    }
+  }
+  .card-item-wrap.flex-card {
+    >.card-item-box:first-child {
+      display: flex;
+      .card-cell-list {
+        flex: 1;
+      }
+    }
+  }
+  .circle-select.mk-extend-icon {
+    margin-right: 35px;
+  }
+
+  .sub-card-wrap.mk-parity-bg {
+    .ant-col:nth-child(even){
+      .card-item-box:not(:hover) {
+        background-color: var(--mk-sys-color1);
+      }
+    }
+  }
+}
+.double-data-card-box::-webkit-scrollbar {
+  width: 7px;
+  height: 7px;
+}
+.double-data-card-box::-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);
+}
+.double-data-card-box::-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);
+}
diff --git a/src/tabviews/custom/components/card/prop-card/index.jsx b/src/tabviews/custom/components/card/prop-card/index.jsx
index 2ad4a02..375b7bf 100644
--- a/src/tabviews/custom/components/card/prop-card/index.jsx
+++ b/src/tabviews/custom/components/card/prop-card/index.jsx
@@ -36,7 +36,6 @@
   UNSAFE_componentWillMount () {
     const { data, initdata } = this.props
     let _config = fromJS(this.props.config).toJS()
-    let _cols = new Map()
 
     let _data = { $$empty: true }
     let _sync = false
@@ -85,11 +84,6 @@
       _data.$$uuid = _data[_config.setting.primaryKey] || ''
     }
 
-    _config.columns.forEach(item => {
-      if (item.type !== 'number') return
-      _cols.set(item.field, item)
-    })
-
     _config.subcards.forEach(card => {
       if (card.setting.click === 'button' && !card.setting.linkbtn) {
         card.elements.forEach(ele => {
@@ -101,18 +95,6 @@
           card.setting.click = ''
         }
       }
-      card.elements = card.elements.map(item => {
-        if (item.eleType === 'number' && item.field && _cols.has(item.field) && typeof(item.decimal) !== 'number') {
-          item.decimal = _cols.get(item.field).decimal || 0
-        }
-        return item
-      })
-      card.backElements = card.backElements.map(item => {
-        if (item.eleType === 'number' && item.field && _cols.has(item.field) && typeof(item.decimal) !== 'number') {
-          item.decimal = _cols.get(item.field).decimal || 0
-        }
-        return item
-      })
     })
 
     let selected = _config.wrap.selected || 'false'
@@ -499,14 +481,14 @@
         <NormalHeader config={config}/>
         <Row className={`card-row-list data-zoom ${config.wrap.wrapClass}`}>
           {config.subcards.map((item, index) => {
-            let className = item.setting.click ? 'mk-card pointer ' : 'mk-card '
+            let className = 'mk-card '
             if (activeKey === index) {
               className += 'active'
             }
 
             return (
-              <Col className={className} key={index} span={item.setting.width || 6} offset={item.offset || 0} onClick={() => {this.changeCard(index, item)}}>
-                <CardItem card={item} cards={config} data={data}/>
+              <Col className={className} key={index} span={item.setting.width || 6} offset={item.offset || 0}>
+                <CardItem card={item} cards={config} data={data} onClick={() => {this.changeCard(index, item)}}/>
               </Col>
             )
           })}
diff --git a/src/tabviews/custom/components/card/prop-card/index.scss b/src/tabviews/custom/components/card/prop-card/index.scss
index f930a0b..87840aa 100644
--- a/src/tabviews/custom/components/card/prop-card/index.scss
+++ b/src/tabviews/custom/components/card/prop-card/index.scss
@@ -12,11 +12,6 @@
     clear: both;
   }
 
-  .card-row-list {
-    .mk-card.pointer {
-      cursor: pointer;
-    }
-  }
   .card-row-list.flex-layout {
     display: flex;
     width: 100%;
diff --git a/src/tabviews/custom/components/card/table-card/index.jsx b/src/tabviews/custom/components/card/table-card/index.jsx
index deb7786..5108474 100644
--- a/src/tabviews/custom/components/card/table-card/index.jsx
+++ b/src/tabviews/custom/components/card/table-card/index.jsx
@@ -43,7 +43,6 @@
   UNSAFE_componentWillMount () {
     const { data, initdata } = this.props
     let _config = fromJS(this.props.config).toJS()
-    let _cols = new Map()
 
     let _data = null
     let _sync = _config.setting.sync === 'true'
@@ -89,20 +88,6 @@
     } else {
       _config.wrap.contentHeight = showHeader ? 'calc(100% - 45px)' : '100%'
     }
-
-    _config.columns.forEach(item => {
-      if (item.type !== 'number') return
-      _cols.set(item.field, item)
-    })
-
-    _config.subcards.forEach(card => {
-      card.elements = card.elements.map(item => {
-        if (item.eleType === 'number' && item.field && _cols.has(item.field) && typeof(item.decimal) !== 'number') {
-          item.decimal = _cols.get(item.field).decimal || 0
-        }
-        return item
-      })
-    })
 
     _config.wrap.pagestyle = _config.wrap.pagestyle || 'page'
 
diff --git a/src/tabviews/custom/components/carousel/data-card/index.jsx b/src/tabviews/custom/components/carousel/data-card/index.jsx
index 9bed413..f67af54 100644
--- a/src/tabviews/custom/components/carousel/data-card/index.jsx
+++ b/src/tabviews/custom/components/carousel/data-card/index.jsx
@@ -38,7 +38,6 @@
     const { data, initdata } = this.props
     let _config = fromJS(this.props.config).toJS()
     let _card = _config.subcards[0]
-    let _cols = new Map()
 
     let _data = null
     let _sync = _config.setting.sync === 'true'
@@ -75,21 +74,9 @@
       })
     }
 
-    _config.columns.forEach(item => {
-      if (item.type !== 'number') return
-      _cols.set(item.field, item)
-    })
-
     if (_card.setting.click) {
       _card.style.cursor = 'pointer'
     }
-
-    _card.elements = _card.elements.map(item => {
-      if (item.eleType === 'number' && item.field && _cols.has(item.field) && typeof(item.decimal) !== 'number') {
-        item.decimal = _cols.get(item.field).decimal || 0
-      }
-      return item
-    })
 
     if (!_config.wrap.height) { // 鍏煎
       _config.wrap.height = _config.style.height || '300px'
diff --git a/src/tabviews/custom/components/carousel/prop-card/index.jsx b/src/tabviews/custom/components/carousel/prop-card/index.jsx
index dde0ca0..ce207c5 100644
--- a/src/tabviews/custom/components/carousel/prop-card/index.jsx
+++ b/src/tabviews/custom/components/carousel/prop-card/index.jsx
@@ -36,7 +36,6 @@
   UNSAFE_componentWillMount () {
     const { data, initdata } = this.props
     let _config = fromJS(this.props.config).toJS()
-    let _cols = new Map()
 
     let _data = {$$empty: true}
     let _sync = false
@@ -79,11 +78,6 @@
     _data.$$BID = BID || ''
     _data.$$BData = BData || ''
 
-    _config.columns.forEach(item => {
-      if (item.type !== 'number') return
-      _cols.set(item.field, item)
-    })
-
     if (!_config.wrap.height) { // 鍏煎
       _config.wrap.height = _config.style.height || '300px'
       delete _config.style.height
@@ -94,12 +88,6 @@
       if (card.setting.click) {
         card.style.cursor = 'pointer'
       }
-      card.elements = card.elements.map(item => {
-        if (item.eleType === 'number' && item.field && _cols.has(item.field) && typeof(item.decimal) !== 'number') {
-          item.decimal = _cols.get(item.field).decimal || 0
-        }
-        return item
-      })
     })
 
     _config.wrap.speed = (_config.wrap.speed || 3) * 1000
diff --git a/src/tabviews/custom/components/code/sand-box/index.jsx b/src/tabviews/custom/components/code/sand-box/index.jsx
index a9edce6..923fd18 100644
--- a/src/tabviews/custom/components/code/sand-box/index.jsx
+++ b/src/tabviews/custom/components/code/sand-box/index.jsx
@@ -97,7 +97,7 @@
       Api.getLCacheConfig(config.uuid).then(res => {
         if (!res || this.loaded) return
 
-        this.setState({data: res[0]}, () => {
+        this.setState({data: res}, () => {
           this.renderView()
         })
       })
@@ -129,13 +129,13 @@
 
       this.loaded = true
 
-      this.setState({sync: false, data: _data})
-
       if (!is(fromJS(this.state.data), fromJS(_data))) {
         setTimeout(() => {
           this.renderView()
         }, 10)
       }
+
+      this.setState({sync: false, data: _data})
     } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
@@ -193,16 +193,16 @@
         Api.writeCacheConfig(config.uuid, result.data || '')
       }
 
-      this.setState({
-        data: _data,
-        loading: false
-      })
-      
       if (!is(fromJS(this.state.data), fromJS(_data))) {
         setTimeout(() => {
           this.renderView()
         }, 10)
       }
+
+      this.setState({
+        data: _data,
+        loading: false
+      })
     } else {
       this.setState({
         loading: false
diff --git a/src/tabviews/custom/components/group/normal-group/index.jsx b/src/tabviews/custom/components/group/normal-group/index.jsx
index c7967b1..631caf0 100644
--- a/src/tabviews/custom/components/group/normal-group/index.jsx
+++ b/src/tabviews/custom/components/group/normal-group/index.jsx
@@ -20,6 +20,7 @@
 const DataCard = asyncComponent(() => import('@/tabviews/custom/components/card/data-card'))
 const TableCard = asyncComponent(() => import('@/tabviews/custom/components/card/table-card'))
 const NormalTable = asyncComponent(() => import('@/tabviews/custom/components/table/normal-table'))
+const DoubleDataCard = asyncComponent(() => import('@/tabviews/custom/components/card/double-data-card'))
 const EditTable = asyncComponent(() => import('@/tabviews/custom/components/table/edit-table'))
 const PropCard = asyncComponent(() => import('@/tabviews/custom/components/card/prop-card'))
 const BraftEditor = asyncComponent(() => import('@/tabviews/custom/components/editor/braft-editor'))
@@ -175,6 +176,12 @@
             <NormalTable config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
+      } else if (item.type === 'card' && item.subtype === 'dualdatacard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <DoubleDataCard config={item} mainSearch={mainSearch}/>
+          </Col>
+        )
       } else if (item.type === 'bar' || item.type === 'line') {
         return (
           <Col span={item.width} style={style} key={item.uuid}>
diff --git a/src/tabviews/custom/components/module/voucher/index.jsx b/src/tabviews/custom/components/module/voucher/index.jsx
index 31b0296..aae840b 100644
--- a/src/tabviews/custom/components/module/voucher/index.jsx
+++ b/src/tabviews/custom/components/module/voucher/index.jsx
@@ -1,18 +1,22 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Button, Select, Input, Modal, DatePicker, notification, InputNumber } from 'antd'
+import { Button, Select, Input, Modal, DatePicker, notification, InputNumber, Dropdown } from 'antd'
 import moment from 'moment'
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
-import asyncComponent from '@/utils/asyncComponent'
 import MKEmitter from '@/utils/events.js'
+import VoucherTable from './voucherTable'
+import SaveAsTemp from './saveAsTemp'
+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
-const { TextArea } = Input
-const VoucherTable = asyncComponent(() => import('./voucherTable'))
 
 class VoucherModule extends Component {
   static propTpyes = {
@@ -34,12 +38,18 @@
     book: null,
     username: sessionStorage.getItem('User_Name'),
     remark: '',
-    remarkVisible: false,
     attachments: 0,
+    attachlist: [],
+    oriAttachs: [],
+    tempTypes: [],
+    tempTypeClass: '',
+    tempTypeName: '',
     title: '',
     orgcode: '',
     orgname: '',
-    status: '' // 鏂板缓鏃讹紝empty銆乧hange銆乻aved
+    status: '', // 鏂板缓鏃讹紝empty銆乧hange銆乻aved
+    saved: false,
+    voucherCode: ''
   }
 
   UNSAFE_componentWillMount () {
@@ -72,11 +82,14 @@
       }
     }
 
+    // config.wrap.type = 'checkVoucher'
+    // BID = '20230214130744811P0K95RQ155KG0QIQOFV'
+
+    // config.wrap.type = 'checkTemp'
+    // BID = '20230214174458780MFR8IA576ON4VKNOLVH'
+
     window.GLOB.CacheVoucher.delete(config.uuid)
     let type = config.wrap.type || 'createVoucher'
-
-    // type = 'checkVoucher'
-    // BID = '202302081418373862P8Q29OUD19CT1AVMU6'
 
     if (type === 'createVoucher' || type === 'createTemp') {
       BID = Utils.getguid()
@@ -175,10 +188,14 @@
           charInt: charInt,
           orgcode: res.orgcode,
           orgname: res.orgname,
+          tempTypes: res.temp_type || []
         })
       } else {
         this.setState({
-          typeOptions: typeOptions
+          typeOptions: typeOptions,
+          orgcode: res.orgcode,
+          orgname: res.orgname,
+          tempTypes: res.temp_type || []
         })
       }
 
@@ -259,6 +276,10 @@
       ID: BID
     }
 
+    if (type === 'checkTemp') {
+      param.func = 's_get_fcc_voucher_temp'
+    }
+
     Api.genericInterface(param).then(res => {
       if (!res.status) {
         notification.warning({
@@ -300,17 +321,47 @@
         })
       }
 
-      this.setState({
-        data: data,
-        vouDate: res.fibvoucherdate ? moment(res.fibvoucherdate, 'YYYY-MM-DD') : null,
-        charType: res.voucher_class,
-        charName: res.voucher_char,
-        charInt: res.voucher_char_int,
-        orgcode: res.orgcode,
-        orgname: res.orgname,
-        tbdata: fromJS(data).toJS(),
-        status: 'saved'
-      })
+      if (type !== 'checkTemp') {
+        let files = []
+
+        res.fcc_files && res.fcc_files.forEach(file => {
+          file.attachments.forEach(item => {
+            item.id = item.attach_id
+            item.data_code = file.data_code
+            item.data_name = file.data_name
+            item.BID = file.id
+
+            files.push(item)
+          })
+        })
+
+        this.setState({
+          data: data,
+          attachlist: files,
+          vouDate: res.fibvoucherdate ? moment(res.fibvoucherdate, 'YYYY-MM-DD') : null,
+          charType: res.voucher_class,
+          charName: res.voucher_char,
+          charInt: res.voucher_char_int,
+          // orgcode: res.orgcode,
+          // orgname: res.orgname,
+          voucherCode: res.voucher_code || '',
+          tbdata: fromJS(data).toJS(),
+          oriAttachs: fromJS(files).toJS(),
+          attachments: res.attachments_int,
+          title: res.voucher_text || '',
+          remark: res.remark || '',
+          status: 'saved'
+        })
+      } else {
+        this.setState({
+          data: data,
+          tbdata: fromJS(data).toJS(),
+          title: res.voucher_text || '',
+          tempTypeClass: res.typechartwo || '',
+          tempTypeName: res.typecharthree || '',
+          status: 'saved'
+        })
+      }
     })
   }
 
@@ -431,12 +482,21 @@
   }
 
   voucherSave = (list, t) => {
-    const { type, data, config, charInt, charType, vouDate, book, remark, charName, attachments, title, orgcode, orgname } = this.state
+    const { type, BID, data, config, voucherCode, charInt, charType, vouDate, book, remark, charName, attachments, title, orgcode, orgname, attachlist, oriAttachs } = this.state
 
+    let err = ''
     if (!book) {
+      err = '璇烽�夋嫨璐﹀锛�'
+    } else if (!vouDate) {
+      err = '璇烽�夋嫨鏃ユ湡锛�'
+    } else if (!charName || !charInt) {
+      err = '璇烽�夋嫨鍑瘉鍙凤紒'
+    }
+
+    if (err) {
       notification.warning({
         top: 92,
-        message: '璇烽�夋嫨璐﹀锛�',
+        message: err,
         duration: 5
       })
       return
@@ -445,8 +505,8 @@
     let param = {
       func: 's_fcc_voucher_addupt',
       BID: book.id,
-      ID: Utils.getguid(),
-      voucher_code: '',
+      ID: BID,
+      voucher_code: voucherCode || '',
       voucher_text: title,
       remark: remark,
       account_year_code: book.account_year_code || '',
@@ -552,8 +612,26 @@
       sup_data.push(`'${n.uuid}','${n.sup_voucher_code}','${n.sup_voucher_lp}','${n.voucher_sup_lp || ''}','${n.sup_subject_code}','${n.sup_subject_name}','${n.sup_voucher_text || ''}','${n.sup_direct}',${n.sup_net_amount},'${n.customercode || ''}','${n.customername || ''}','${n.suppliercode || ''}','${n.suppliername || ''}','${n.co_pro_code || ''}','${n.co_pro_name || ''}','${n.workercode || ''}','${n.workername || ''}','${n.project_code || ''}','${n.project_name || ''}','${n.productcode || ''}','${n.productname || ''}','${n.cash_flow_code || ''}','${n.cash_flow_name || ''}','${n.sup_acc_code || ''}','${n.sup_acc_name || ''}','${n.sup_acc_type || ''}','${n.sup_bid}',1`)
     })
 
+    let attachments_data = []
+    let ids = []
+
+    attachlist.forEach(item => {
+      ids.push(item.id)
+
+      attachments_data.push(`'${item.id}',0`)
+    })
+
+    if (oriAttachs.length > 0) {
+      oriAttachs.forEach(item => {
+        if (!ids.includes(item.id)) {
+          attachments_data.push(`'${item.id}',1`)
+        }
+      })
+    }
+
     param.subject_data = window.btoa(window.encodeURIComponent(subject_data.join(';un')))
     param.sup_data = window.btoa(window.encodeURIComponent(sup_data.join(';un')))
+    param.attachments_data = window.btoa(window.encodeURIComponent(attachments_data.join(';un')))
 
     Api.genericInterface(param).then(res => {
       if (!res.status) {
@@ -576,38 +654,258 @@
           status: 'empty',
           remark: '',
           tbdata: [],
+          oriAttachs: fromJS(attachlist).toJS(),
           charInt: res.voucher_char_int + 1,
-          BID: Utils.getguid()
+          BID: Utils.getguid(),
+          saved: false
         })
         MKEmitter.emit('cleartable', config.uuid)
       } else {
         this.setState({
           status: 'saved',
           charInt: res.voucher_char_int || charInt,
-          data: fromJS(list).toJS()
+          data: fromJS(list).toJS(),
+          oriAttachs: fromJS(attachlist).toJS(),
+          saved: true
         })
       }
     })
   }
 
-  triggerprint = () => {
-    this.setState({remarkVisible: true})
-  }
+  triggerTempsave = (name, typeChar, typeName) => {
+    const { tbdata } = this.state
 
-  remarkSubmit = () => {
-    const { config } = this.state
-    let node = document.getElementById(config.uuid + 'remark')
-    let val = node.value
+    let err = ''
+    let list = []
 
-    if (val && val.length > 512) {
+    tbdata.forEach((line, index) => {
+      if (err) return
+
+      let _index = index + 1
+
+      if (!line.subject_voucher_text && !line.subject_code) {
+        if (_index === 1) {
+          err = '绗�1琛屼笉鍙负绌恒��'
+        }
+
+        return
+      }
+
+      if (!line.subject_voucher_text) {
+        err = `绗�${_index}琛岋紝鎽樿涓嶅彲涓虹┖銆俙
+      } else if (!line.subject_code) {
+        err = `绗�${_index}琛岋紝绉戠洰涓嶅彲涓虹┖銆俙
+      } else if (line.sup_accounting && !line.supAccounts) {
+        err = `绗�${_index}琛岋紝璇烽�夋嫨杈呭姪鏍哥畻銆俙
+      } else if (line.sup_accounting && line.supAccounts) {
+        line.supAccounts.forEach(item => {
+          if (item.sup_acc_type === 'supplier') {
+            if (!item.suppliercode || !item.suppliername) {
+              err = `绗�${_index}琛岋紝璇烽�夋嫨杈呭姪鏍哥畻銆俙
+            }
+          } else if (item.sup_acc_type === 'customer') {
+            if (!item.customercode || !item.customername) {
+              err = `绗�${_index}琛岋紝璇烽�夋嫨杈呭姪鏍哥畻銆俙
+            }
+          } else if (item.sup_acc_type === 'department') {
+            if (!item.co_pro_code || !item.co_pro_name) {
+              err = `绗�${_index}琛岋紝璇烽�夋嫨杈呭姪鏍哥畻銆俙
+            }
+          } else if (item.sup_acc_type === 'project') {
+            if (!item.projectcode || !item.projectname) {
+              err = `绗�${_index}琛岋紝璇烽�夋嫨杈呭姪鏍哥畻銆俙
+            }
+          } else if (item.sup_acc_type === 'inventory') {
+            if (!item.productcode || !item.productname) {
+              err = `绗�${_index}琛岋紝璇烽�夋嫨杈呭姪鏍哥畻銆俙
+            }
+          } else if (item.sup_acc_type === 'employee') {
+            if (!item.workercode || !item.workername) {
+              err = `绗�${_index}琛岋紝璇烽�夋嫨杈呭姪鏍哥畻銆俙
+            }
+          } else if (item.sup_acc_type === 'cash_flow') {
+            if (!item.cash_flow_code || !item.cash_flow_name) {
+              err = `绗�${_index}琛岋紝璇烽�夋嫨杈呭姪鏍哥畻銆俙
+            }
+          } else if (!item.sup_acc_type || !item.sup_acc_code || !item.sup_acc_name) {
+            err = `绗�${_index}琛岋紝璇烽�夋嫨杈呭姪鏍哥畻銆俙
+          }
+        })
+      }
+
+      list.push(line)
+    })
+
+    if (!err && list.length === 0) {
+      err = '绗�1琛屼笉鍙负绌恒��'
+    }
+
+    if (err) {
       notification.warning({
         top: 92,
-        message: '褰撳墠鍐呭瓒呴暱锛屽娉ㄦ渶澶�512涓瓧绗︺��',
+        message: err,
         duration: 5
       })
       return
     }
-    this.setState({remark: val, remarkVisible: false})
+    
+    this.voucherTempSave(list, name, typeChar, typeName)
+  }
+
+  voucherTempSave = (list, name, typeChar, typeName) => {
+    const { type, BID, data, book, title, orgcode, orgname, tempTypeClass, tempTypeName } = this.state
+
+    if (!book) {
+      notification.warning({
+        top: 92,
+        message: '璇烽�夋嫨璐﹀锛�',
+        duration: 5
+      })
+      return
+    } else if (type !== 'createVoucher' && !title) {
+      notification.warning({
+        top: 92,
+        message: '璇峰~鍐欐ā鏉垮悕绉帮紒',
+        duration: 5
+      })
+      return
+    } else if (type !== 'createVoucher' && !tempTypeClass) {
+      notification.warning({
+        top: 92,
+        message: '璇烽�夋嫨妯℃澘绫诲瀷锛�',
+        duration: 5
+      })
+      return
+    }
+
+    let id = BID
+    let _name = title
+    let _typeChar = tempTypeClass
+    let _typeName = tempTypeName
+
+    if (type === 'createVoucher') {
+      id = Utils.getguid()
+      _name = name
+      _typeChar = typeChar
+      _typeName = typeName
+    }
+
+    let param = {
+      func: 's_fcc_voucher_addupt_temp',
+      BID: book.id,
+      ID: id,
+      voucher_code: '',
+      voucher_text: _name,
+      remark: '',
+      account_year_code: book.account_year_code || '',
+      voucher_type: 'fcc_temp',
+      voucher_type_text: '鍑瘉妯℃澘',
+      orgcode: orgcode || '',
+      orgname: orgname || '',
+      voucher_class: 'temp',
+      years: book.years,
+      months: moment().format('YYYY-MM'),
+      business_type: '',
+      voucher_sign: 'temp',
+      voucher_char: '',
+      voucher_char_int: 0,
+      account_code: book.account_code || '',
+      fibvoucherdate: moment().format('YYYY-MM-DD'),
+      UserName: sessionStorage.getItem('User_Name') || '',
+      FullName: sessionStorage.getItem('Full_Name') || '',
+      attachments_int: 0,
+      typechartwo: _typeChar,
+      typecharthree: _typeName,
+      sup_data: '',
+      subject_data: ''
+    }
+
+    let sup_data = []
+    let voucherMap = new Map()
+    let supMap = new Map()
+
+    if (type !== 'createVoucher') {
+      data.forEach(item => {
+        voucherMap.set(item.uuid, item)
+  
+        if (item.sup_accounting && item.supAccounts) {
+          item.supAccounts.forEach(cell => {
+            if (!cell.sup_acc_type) return
+  
+            let _cell = {...cell}
+  
+            _cell.sup_voucher_code = item.subject_voucher_code || ''
+            _cell.sup_voucher_lp = item.voucher_lp || ''
+            _cell.sup_subject_code = item.subject_code || ''
+            _cell.sup_subject_name = item.subject_name || ''
+            _cell.sup_bid = item.uuid || ''
+            _cell.sup_direct = !item.credit ? 'debit' : 'credit'
+            _cell.sup_net_amount = item.debit || item.credit || 0
+  
+            supMap.set(item.uuid + cell.sup_acc_type, _cell)
+          })
+        }
+      })
+    }
+    
+    let subject_data = list.map(item => {
+      let count = item.count_type === 'Y'
+      let curr = item.foreign_currency_type === 'Y'
+      let direct = !item.credit ? 'debit' : 'credit'
+
+      if (voucherMap.has(item.uuid)) {
+        voucherMap.delete(item.uuid)
+      }
+      if (item.sup_accounting && item.supAccounts) {
+        item.supAccounts.forEach(n => {
+          if (supMap.has(item.uuid + n.sup_acc_type)) {
+            supMap.delete(item.uuid + n.sup_acc_type)
+          }
+
+          sup_data.push(`'${n.uuid}','${item.subject_voucher_code || ''}','${item.voucher_lp || ''}','${n.voucher_sup_lp || ''}','${item.subject_code}','${item.subject_name}','${n.sup_voucher_text || ''}','${direct}',${item.debit || item.credit || 0},'${n.customercode || ''}','${n.customername || ''}','${n.suppliercode || ''}','${n.suppliername || ''}','${n.co_pro_code || ''}','${n.co_pro_name || ''}','${n.workercode || ''}','${n.workername || ''}','${n.project_code || ''}','${n.project_name || ''}','${n.productcode || ''}','${n.productname || ''}','${n.cash_flow_code || ''}','${n.cash_flow_name || ''}','${n.sup_acc_code || ''}','${n.sup_acc_name || ''}','${n.sup_acc_type || ''}','${item.uuid}',0`)
+        })
+      }
+      return `'${item.uuid}','${item.subject_voucher_code || ''}','${item.voucher_lp || ''}','${item.subject_code}','${item.subject_name}','${item.subject_voucher_text || ''}',${count ? item.fcc_count || 0 : 0},${count ? item.net_unitprice || 0 : 0},'${item.unit}',${item.debit || item.credit || 0},'${direct}','${curr ? item.exratecode : '01010001'}','${curr ? item.exratename : 'CNY'}',${curr ? item.unitratio || 0 : 0},'${item.sup_accounting}',${item.direct ? 1 : -1},${curr ? item.foreign_amount || 0 : 0},0,'${item.local_currency || ''}','${count ? 'Y' : ''}','${curr ? 'Y' : ''}'`
+    })
+
+    voucherMap.forEach(item => {
+      let count = item.count_type === 'Y'
+      let curr = item.foreign_currency_type === 'Y'
+      let direct = !item.credit ? 'debit' : 'credit'
+
+      subject_data.push(`'${item.uuid}','${item.subject_voucher_code || ''}','${item.voucher_lp || ''}','${item.subject_code}','${item.subject_name}','${item.subject_voucher_text || ''}',${count ? item.fcc_count || 0 : 0},${count ? item.net_unitprice || 0 : 0},'${item.unit}',${item.debit || item.credit || 0},'${direct}','${curr ? item.exratecode : '01010001'}','${curr ? item.exratename : 'CNY'}',${curr ? item.unitratio || 0 : 0},'${item.sup_accounting}',${item.direct ? 1 : -1},${curr ? item.foreign_amount || 0 : 0},1,'${item.local_currency || ''}','${count ? 'Y' : ''}','${curr ? 'Y' : ''}'`)
+    })
+
+    supMap.forEach(n => {
+      sup_data.push(`'${n.uuid}','${n.sup_voucher_code}','${n.sup_voucher_lp}','${n.voucher_sup_lp || ''}','${n.sup_subject_code}','${n.sup_subject_name}','${n.sup_voucher_text || ''}','${n.sup_direct}',${n.sup_net_amount},'${n.customercode || ''}','${n.customername || ''}','${n.suppliercode || ''}','${n.suppliername || ''}','${n.co_pro_code || ''}','${n.co_pro_name || ''}','${n.workercode || ''}','${n.workername || ''}','${n.project_code || ''}','${n.project_name || ''}','${n.productcode || ''}','${n.productname || ''}','${n.cash_flow_code || ''}','${n.cash_flow_name || ''}','${n.sup_acc_code || ''}','${n.sup_acc_name || ''}','${n.sup_acc_type || ''}','${n.sup_bid}',1`)
+    })
+
+    param.subject_data = window.btoa(window.encodeURIComponent(subject_data.join(';un')))
+    param.sup_data = window.btoa(window.encodeURIComponent(sup_data.join(';un')))
+
+    Api.genericInterface(param).then(res => {
+      if (!res.status) {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+        return
+      }
+
+      notification.success({
+        top: 92,
+        message: '淇濆瓨鎴愬姛锛�',
+        duration: 5
+      })
+
+      if (type !== 'createVoucher') {
+        this.setState({
+          status: 'saved',
+          data: fromJS(list).toJS(),
+        })
+      }
+    })
   }
 
   dataChange = (data) => {
@@ -626,7 +924,93 @@
       _val = parseInt(val)
     }
     
-    this.setState({attachments: _val})
+    this.setState({attachments: _val, status: 'change'})
+  }
+
+  changeVouDate = (val) => {
+    const { type, status, saved } = this.state
+
+    this.setState({vouDate: val, status: 'change'})
+
+    if (type === 'createVoucher' && val && !saved && (status === 'empty' || status === 'change')) {
+      this.updateVoucherChar(val)
+    }
+  }
+
+  updateVoucherChar = (val) => {
+    const { book, config } = this.state
+
+    if (!book) return
+
+    let param = {
+      func: 's_get_fcc_account_data',
+      search_type: 'Y',
+      fcc_date: book.months ? book.months + '-01' : moment().format('YYYY-MM-DD'),
+      account_year_code: book.account_year_code || '',
+      months: moment(val).format('YYYY-MM'),
+      BID: book.id
+    }
+
+    Api.genericInterface(param).then(res => {
+      if (!res.status) {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+        return
+      }
+
+      let typeOptions = res.char || []
+
+      if (typeOptions.length > 0) {
+        let charType = this.state.charType
+        let charName = this.state.charName
+        let charInt = this.state.charInt
+  
+        if (charType && typeOptions.filter(n => n.voucher_class === charType) > 0) {
+          typeOptions.forEach(n => {
+            if (n.voucher_class === charType) {
+              charName = n.voucher_char
+              charInt = n.voucher_char_int + 1
+            }
+          })
+        } else {
+          charType = typeOptions[0].voucher_class
+          charName = typeOptions[0].voucher_char
+          charInt = typeOptions[0].voucher_char_int + 1
+        }
+
+        this.setState({
+          typeOptions: typeOptions,
+          charType: charType,
+          charName: charName,
+          charInt: charInt
+        })
+      }
+
+      let msg = window.GLOB.CacheVoucher.get(config.uuid) || {}
+      msg.currency = res.currency || []
+
+      window.GLOB.CacheVoucher.set(config.uuid, msg)
+    })
+  }
+
+  resetAttachList = (vals) => {
+    const { attachlist } = this.state
+    let num = this.state.attachments
+    
+    if (num) {
+      num = num + (vals.length - attachlist.length)
+    } else {
+      num = vals.length
+    }
+
+    if (num < 0) {
+      num = 0
+    }
+
+    this.setState({status: 'change', attachlist: vals, attachments: num})
   }
 
   triggermore = () => {
@@ -634,65 +1018,145 @@
   }
 
   triggerclose = () => {
+    const { config, status } = this.state
 
+    if (status === 'change') {
+      confirm({
+        content: '鍐呭宸插彉鏇达紝纭畾瑕佸叧闂悧锛�',
+        onOk() {
+          MKEmitter.emit('closeTabView', config.$pageId)
+        },
+        onCancel() {}
+      })
+    } else {
+      MKEmitter.emit('closeTabView', config.$pageId)
+    }
   }
 
-  triggerAttach = () => {
+  triggerTempLoad = (id) => {
+    const { book } = this.state
 
+    if (!book) return
+
+    let param = {
+      func: 's_get_fcc_voucher_temp',
+      BID: book.id,
+      ID: id
+    }
+
+    Api.genericInterface(param).then(res => {
+      if (!res.status) {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+        return
+      }
+
+      let data = []
+      if (res.voucher) {
+        data = res.voucher.map(line => {
+          line.uuid = line.subject_id || ''
+
+          if (line.direction_type === 'credit') {
+            line.credit = line.net_amount || 0
+            line.debit = ''
+          } else {
+            line.debit = line.net_amount || 0
+            line.credit = ''
+          }
+
+          line.unitratio = line.foreign_unitratio || 0
+          line.exratecode = line.foreign_exratecode || ''
+          line.exratename = line.foreign_exratename || ''
+          line.local_currency = line.local_exratecode || ''
+          line.foreign_currency_type = line.foreign_type || ''
+
+          if (line.sup) {
+            line.supAccounts = line.sup.map(cell => {
+              cell.uuid = cell.sup_id
+              return cell
+            })
+            delete line.sup
+          }
+
+          return line
+        })
+      }
+
+      this.setState({
+        data: data,
+        tbdata: fromJS(data).toJS(),
+        status: 'change'
+      })
+    })
   }
 
   render() {
-    const { type, status, loading, config, typeOptions, charType, charInt, data, vouDate, username, remark, remarkVisible, attachments, title } = this.state
+    const { type, status, loading, config, orgcode, orgname, typeOptions, tempTypes, charType, charName, charInt, data, vouDate, username, remark, attachments, title, attachlist, tempTypeClass } = 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>
-          <Button className="print-background header-btn" disabled={status !== 'saved'} onClick={this.triggerprint}>鎵撳嵃</Button>
-          <Button className="out-background header-btn" onClick={this.triggermore}>鏇村</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>
-          <Button className="print-background header-btn" disabled={status !== 'saved'} onClick={this.triggerprint}>鎵撳嵃</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">
+        <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({charType: val, charName: option.props.charName, charInt: option.props.charint})}>
+              <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.voucher_char_int} 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({charInt: val})}/> 鍙�
+              <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={(val) => this.setState({vouDate: val})}/>
+              鏃ユ湡锛�<DatePicker value={vouDate} onChange={this.changeVouDate}/>
             </div>
             <div className="voucher-text">
-              <Input value={title} placeholder="鍑瘉鏂囨湰" autoComplete="off" onChange={(e) => this.setState({title: e.target.value})}/>
+              <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}/> 寮�
-              <Button type="link" className="" onClick={this.triggerAttach}>闄勪欢</Button>
-              <Button type="link" className="" onClick={this.triggerprint}>澶囨敞</Button>
+              <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="add-background header-btn" onClick={() => this.triggerTempsave()}>淇濆瓨</Button>
+              <Button className="out-background header-btn" onClick={this.triggerclose}>鍏抽棴</Button>
             </div>
           </div> : null}
           <VoucherTable config={config} loading={loading} data={data} onChange={this.dataChange}/>
         </div>
-        <div className="user">鍒跺崟浜猴細{username}</div>
-        <Modal
-          title="澶囨敞"
-          visible={remarkVisible}
-          width={700}
-          maskClosable={false}
-          onOk={this.remarkSubmit}
-          onCancel={() => { this.setState({ remarkVisible: false })}}
-          destroyOnClose
-        >
-          <TextArea id={config.uuid + 'remark'} defaultValue={remark} rows={6}/>
-        </Modal>
+        {type === 'createVoucher' || type === 'checkVoucher' ? <div className="user">鍒跺崟浜猴細{username}</div> : null}
       </div>
     )
   }
diff --git a/src/tabviews/custom/components/module/voucher/index.scss b/src/tabviews/custom/components/module/voucher/index.scss
index aa48f4d..a2bae1b 100644
--- a/src/tabviews/custom/components/module/voucher/index.scss
+++ b/src/tabviews/custom/components/module/voucher/index.scss
@@ -7,6 +7,7 @@
   background-size: cover;
   min-height: 150px;
   overflow-y: auto;
+  overflow-x: hidden;
   color: #000000;
 
   .voucher-header {
@@ -20,7 +21,6 @@
     }
   }
   .voucher-body {
-    padding: 0 30px;
     .voucher-code {
       display: inline-block;
       width: 160px;
@@ -71,6 +71,28 @@
         width: 60px;
       }
     }
+    .pre-temp-wrap {
+      padding: 10px 0px;
+      .temp-text {
+        display: inline-block;
+        width: 22%;
+        white-space: nowrap;
+        padding-left: 10px;
+
+        .ant-input {
+          width: calc(100% - 70px);
+        }
+        .ant-select {
+          width: calc(100% - 70px);
+        }
+      }
+      .temp-action {
+        float: right;
+        .ant-btn {
+          margin-left: 15px;
+        }
+      }
+    }
   }
 
   .add-background {
@@ -107,3 +129,30 @@
     display: none;
   }
 }
+.mk-voucher-dropdown-wrap {
+  background: #ffffff;
+  border: 1px solid #d8d8d8;
+  border-radius: 4px;
+  min-height: 66px;
+  .ant-btn {
+    display: block;
+    width: 100%;
+    border: none;
+  }
+  .split {
+    width: 100%;
+    height: 1px;
+    background: #d8d8d8;
+  }
+}
+.mk-temp-add-modal {
+  .ant-form-item {
+    .ant-form-item-label {
+      width: 170px;
+    }
+    .ant-form-item-control-wrapper {
+      width: 250px;
+      display: inline-block;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/loadFromTemp/index.jsx b/src/tabviews/custom/components/module/voucher/loadFromTemp/index.jsx
new file mode 100644
index 0000000..a04a8ae
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/loadFromTemp/index.jsx
@@ -0,0 +1,103 @@
+import React, {Component} from 'react'
+import { Button, Modal, notification, Empty, Input } from 'antd'
+
+import './index.scss'
+
+const { Search } = Input
+
+class LoadFromTemp extends Component {
+  state = {
+    visible: false,
+    tempId: '',
+    searchkey: '',
+    temps: []
+  }
+
+  submit = () => {
+    const { tempId } = this.state
+
+    if (!tempId) {
+      notification.warning({
+        top: 92,
+        message: '璇烽�夋嫨妯℃澘锛�',
+        duration: 5
+      })
+      return
+    }
+
+    this.setState({visible: false})
+    this.props.onChange(tempId)
+  }
+
+  trigger = () => {
+    const { tempTypes } = this.props
+    let temps = []
+
+    tempTypes.forEach(item => {
+      if (item.temp_list && item.temp_list.length > 0) {
+        item.temp_list.forEach(cell => {
+          temps.push({
+            id: cell.temp_id,
+            voucher_text: cell.voucher_text || '',
+            data_name: item.data_name || ''
+          })
+        })
+      }
+    })
+
+    this.setState({visible: true, tempId: '', temps, searchkey: ''})
+  }
+
+  checkItem = (id) => {
+    this.setState({tempId: id})
+  }
+
+  render() {
+    const { visible, tempId, temps, searchkey } = this.state
+
+    let _temps = temps
+
+    if (searchkey) {
+      _temps = temps.filter(item => item.voucher_text.indexOf(searchkey) > -1 || item.data_name.indexOf(searchkey) > -1)
+    }
+
+    return (
+      <>
+        <Button onClick={() => this.trigger()}>浠庢ā鏉夸腑鍔犺浇</Button>
+        <Modal
+          title="浠庢ā鏉夸腑鍔犺浇"
+          wrapClassName="mk-temp-list-wrap"
+          visible={visible}
+          width={700}
+          maskClosable={false}
+          onOk={this.submit}
+          onCancel={() => { this.setState({ visible: false })}}
+          destroyOnClose
+        >
+          {visible ? <div className="document-wrap">
+            <Search placeholder="" defaultValue="" style={{ minWidth: '200px' }} onSearch={(val) => this.setState({searchkey: val, tempId: ''})}/>
+            <div className="document-title">
+              <div className="folder-box">妯℃澘绫诲瀷</div>
+              <div className="folder">
+                妯℃澘鍚嶇О
+              </div>
+            </div>
+            <div className="document-body">
+              {_temps.length ? <div className="file-wrap">
+                {_temps.map(doc => {
+                  return <div className="file-item" onClick={() => this.checkItem(doc.id)} key={doc.id}>
+                    <span className={'square-select' + (tempId === doc.id ? ' active' : '')}></span>
+                    <span className="folder-name">{doc.data_name}</span>
+                    <span className="file-name">{doc.voucher_text}</span>
+                  </div>
+                })}
+              </div> : <Empty style={{padding: '10px 0px'}} description={null}/>}
+            </div>
+          </div> : null}
+        </Modal>
+      </>
+    )
+  }
+}
+
+export default LoadFromTemp
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/loadFromTemp/index.scss b/src/tabviews/custom/components/module/voucher/loadFromTemp/index.scss
new file mode 100644
index 0000000..17e09d8
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/loadFromTemp/index.scss
@@ -0,0 +1,117 @@
+.mk-temp-list-wrap {
+  .ant-modal-body {
+    padding: 15px 20px;
+  }
+  .document-wrap {
+    .ant-input-search {
+      width: 200px;
+      margin-bottom: 5px;
+    }
+    .document-title {
+      display: flex;
+      background: #e8e8e8;
+      line-height: 38px;
+      border: 1px solid #d8d8d8;
+      border-bottom: none;
+      padding-left: 38px;
+      .folder-box {
+        width: 200px;
+        text-align: center;
+      }
+      .folder {
+        flex: 1;
+        text-align: center;
+      }
+    }
+    .document-body {
+      border: 1px solid #d8d8d8;
+      height: 300px;
+      .file-wrap {
+        width: 100%;
+        height: 100%;
+        overflow-y: auto;
+        .file-item {
+          border-bottom: 1px solid #e8e8e8;
+          height: 40px;
+          line-height: 40px;
+          display: flex;
+
+          .square-select {
+            top: 12px;
+            margin: 0 10px;
+          }
+          .folder-name {
+            display: inline-block;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            vertical-align: top;
+            text-align: center;
+            width: 200px;
+            padding: 0 5px;
+          }
+          .file-name {
+            flex: 1;
+            display: inline-block;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            vertical-align: top;
+            text-align: center;
+            width: calc(100% - 200px);
+            padding: 0 5px;
+          }
+        }
+      }
+    }
+
+    .file-wrap::-webkit-scrollbar {
+      width: 7px;
+    }
+    .file-wrap::-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);
+    }
+    .file-wrap::-webkit-scrollbar-track {
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      border: 1px solid rgba(0, 0, 0, 0.07);
+      background: rgba(0, 0, 0, 0);
+    }
+
+    .square-select {
+      position: relative;
+      display: inline-block;
+      width: 16px;
+      height: 16px;
+      border: 1px solid #cccccc;
+      box-sizing: content-box;
+      border-radius: 16px;
+      margin: auto;
+      margin-right: 5px;
+      background-color: #ffffff;
+      transition: border-color 0.2s;
+      cursor: pointer;
+    }
+
+    .square-select.active {
+      border-color: var(--mk-sys-color);
+      background: var(--mk-sys-color);
+    }
+
+    .square-select::before {
+      position: relative;
+      top: 1px;
+      left: 6px;
+      content: ' ';
+      display: block;
+      width: 5px;
+      height: 11px;
+      border-style: solid;
+      border-width: 0 2px 2px 0;
+      border-color: #ffffff;
+      transform: rotate(45deg);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/printVoucher/index.jsx b/src/tabviews/custom/components/module/voucher/printVoucher/index.jsx
new file mode 100644
index 0000000..d4fcd37
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/printVoucher/index.jsx
@@ -0,0 +1,354 @@
+import React, {Component} from 'react'
+import { Button } from 'antd'
+import moment from 'moment'
+import html2Canvas from 'html2canvas'
+import JsPDF from 'jspdf'
+
+import './index.scss'
+
+class printVoucher extends Component {
+  state = {
+    loading: false
+  }
+
+  trigger = () => {
+    this.setState({loading: true})
+
+    setTimeout(() => {
+      this.getCanvas()
+    }, 200)
+  }
+
+  getCanvas = () => {
+    const { ID } = this.props
+    let wrap = document.getElementById(ID)
+    let elements = wrap.querySelectorAll('.mk-voucher-print-pdf-wrap')
+    let  pageArr = []
+    const opts = {
+      scale: 1.5,        // 缂╂斁姣斾緥锛屾彁楂樼敓鎴愬浘鐗囨竻鏅板害
+      useCORS: false,    // 鍏佽鍔犺浇璺ㄥ煙鐨勫浘鐗�
+      allowTaint: false, // 鍏佽鍥剧墖璺ㄥ煙锛屽拰 useCORS 浜岃�呬笉鍙叡鍚屼娇鐢�
+      tainttest: false,  // 妫�娴嬫瘡寮犲浘鐗囧凡缁忓姞杞藉畬鎴�
+      logging: false     // 鏃ュ織寮�鍏筹紝鍙戝竷鐨勬椂鍊欒寰楁敼鎴� false
+    }
+    
+    elements = Array.from(elements)
+
+    for (let index = 0; index < elements.length; index++) {
+      // eslint-disable-next-line
+      html2Canvas(elements[index], opts).then(canvas => {
+        // a4绾哥殑灏哄[595.28,841.89]锛宧tml椤甸潰鐢熸垚鐨刢anvas鍦╬df涓浘鐗囩殑瀹介珮
+        const contentWidth = canvas.width
+        const contentHeight = canvas.height
+        const imgWidth = 595.28
+        const imgHeight = (592.28 / contentWidth) * contentHeight
+        const pageData = canvas.toDataURL('image/jpeg', 1.0)
+        // 涓�椤祊df鏄剧ずhtml椤甸潰鐢熸垚鐨刢anvas楂樺害;
+        const pageHeight = (contentWidth / 592.28) * 841.89
+        // 鏈敓鎴恜df鐨刪tml椤甸潰楂樺害
+        const leftHeight = contentHeight
+        pageArr.push({ pageData: pageData, pageHeight: pageHeight, leftHeight: leftHeight, imgWidth: imgWidth, imgHeight: imgHeight })
+
+        if (pageArr.length === elements.length) {
+          this.getPdf(pageArr)
+        }
+      })
+    }
+  }
+
+  getPdf = (pageArr) => {
+    const { orgname, vouDate, charName, charInt } = this.props
+    let title = moment(vouDate).format('YYYY-MM-DD') + charName + '-' + charInt + orgname + moment().format('YYYYMMDDHHmmss')
+    const PDF = new JsPDF('', 'pt', 'a4')
+
+    pageArr.forEach((data, index) => {
+      // 椤甸潰鍋忕Щ
+      let position = 0
+      // 杞崲瀹屾瘯锛宻ave淇濆瓨鍚嶇О鍚庢祻瑙堝櫒浼氳嚜鍔ㄤ笅杞�
+      // 褰撳唴瀹规湭瓒呰繃pdf涓�椤垫樉绀虹殑鑼冨洿锛屾棤闇�鍒嗛〉
+      if (data.leftHeight < data.pageHeight) {
+        // addImage(pageData, 'JPEG', 宸︼紝涓婏紝瀹藉害锛岄珮搴�)璁剧疆
+        PDF.addImage(data.pageData, 'JPEG', 0, 0, data.imgWidth, data.imgHeight)
+      } else {
+        // 瓒呰繃涓�椤垫椂锛屽垎椤垫墦鍗帮紙姣忛〉楂樺害841.89锛�
+        while (data.leftHeight > 0) {
+          PDF.addImage(data.pageData, 'JPEG', 0, position, data.imgWidth, data.imgHeight)
+          data.leftHeight -= data.pageHeight
+          position -= 841.89
+          if (data.leftHeight > 0) {
+            PDF.addPage()
+          }
+        }
+      }
+
+      if (index + 1 === pageArr.length) {
+        PDF.save(title + '.pdf')
+
+        this.setState({loading: false})
+      } else {
+        // 鏈浆鎹㈠埌鏈�鍚庝竴椤垫椂锛宲df澧炲姞涓�椤�
+        PDF.addPage()
+      }
+    })
+  }
+
+  getContent = () => {
+    const { data, orgname, vouDate, charName, charInt, attachments } = this.props
+
+    let list = []
+    let items = []
+    let index = 0
+
+    data.forEach((cell, i) => {
+      if (index < 10) {
+        items.push(cell)
+        index++
+      } else {
+        list.push([...items])
+        index = 0
+        items = []
+      }
+    })
+
+    if (items.length > 0) {
+      while (items.length < 5) {
+        items.push({uuid: index + ''})
+        index++
+      }
+
+      list.push(items)
+    } else if (list.length === 0) {
+      list = [[{uuid: '1'}, {uuid: '2'}, {uuid: '3'}, {uuid: '4'}, {uuid: '5'}]]
+    }
+
+    let total = this.getTotalLine(data)
+
+    if (total.debit) {
+      total.debit = total.debit.toFixed(2)
+    }
+    if (total.credit) {
+      total.credit = total.credit.toFixed(2)
+    }
+
+    return <>
+      {list.map((page, i) => (<div className="mk-voucher-print-pdf-wrap" key={i}>
+        <div className="print-header">
+          <div className="line">
+            <div className="left"></div>
+            <div className="center">璁拌处鍑瘉</div>
+            <div className="right">闄勫崟鎹暟锛歿attachments}</div>
+          </div>
+          <div className="line">
+            <div className="left">鍗曚綅锛歿orgname}</div>
+            <div className="center">鏃ユ湡锛歿moment(vouDate).format('YYYY-MM-DD')}</div>
+            <div className="right">鍑瘉鍙凤細{charName}-{charInt}{list.length > 1 ? `(${i + 1}/${list.length})` : ''}</div>
+          </div>
+        </div>
+        <div className="print-body">
+          <div className="print-line" key="0">
+            <div className="remark" style={{textAlign: 'center'}}>鎽樿</div>
+            <div className="subject" style={{textAlign: 'center'}}>绉戠洰</div>
+            <div className="credit" style={{textAlign: 'center'}}>鍊熸柟閲戦</div>
+            <div className="credit" style={{textAlign: 'center'}}>璐锋柟閲戦</div>
+          </div>
+          {page.map(record => {
+            let remark = record.subject_voucher_text || ''
+
+            if (record.count_type === 'Y') {
+              remark += `锛堟暟閲�:${record.fcc_count !== undefined ? record.fcc_count : ''}锛屽崟浠�:${record.net_unitprice !== undefined ? record.net_unitprice : ''}锛塦
+            }
+            if (record.foreign_currency_type === 'Y') {
+              remark += `锛堣揣甯侊細${record.exratename || ''}锛屾眹鐜囷細${record.unitratio !== undefined ? record.unitratio : ''}锛屽師甯侊細${record.foreign_amount !== undefined ? record.foreign_amount : ''}锛塦
+            }
+
+            let val = ''
+            if (record.subject_code) {
+              val = (record.subject_code || '') + ' ' + (record.subject_name || '')
+  
+              if (record.sup_accounting && record.supAccounts) {
+                record.supAccounts.forEach(item => {
+                  if (item.sup_acc_type === 'supplier') {
+                    val += item.suppliername ? '_'  + item.suppliername : ''
+                  } else if (item.sup_acc_type === 'customer') {
+                    val += item.customername ? '_'  + item.customername : ''
+                  } else if (item.sup_acc_type === 'department') {
+                    val += item.co_pro_name ? '_' + item.co_pro_name : ''
+                  } else if (item.sup_acc_type === 'project') {
+                    val += item.projectname ? '_' + item.projectname : ''
+                  } else if (item.sup_acc_type === 'inventory') {
+                    val += item.productname ? '_' + item.productname : ''
+                  } else if (item.sup_acc_type === 'employee') {
+                    val += item.workername ? '_' + item.workername : ''
+                  } else if (item.sup_acc_type === 'cash_flow') {
+                    val += item.cash_flow_name ? '_' + item.cash_flow_name : ''
+                  } else if (item.sup_acc_name) {
+                    val += '_' + item.sup_acc_name
+                  }
+                })
+              }
+            }
+
+            let debit = record.debit || record.debit === 0 ? record.debit : ''
+            let credit = record.credit || record.credit === 0 ? record.credit : ''
+
+            if (debit) {
+              debit = debit.toFixed(2)
+            }
+            if (credit) {
+              credit = credit.toFixed(2)
+            }
+
+            return <div className="print-line" key={record.uuid}>
+              <div className="remark">{remark}</div>
+              <div className="subject">{val}</div>
+              <div className="credit">{debit}</div>
+              <div className="credit">{credit}</div>
+            </div>
+          })}
+          <div className="print-line" key="total">
+            <div className="remark" style={{width: '60%'}}>鍚堣锛歿total.subject_voucher_text}</div>
+            <div className="credit">{total.debit}</div>
+            <div className="credit">{total.credit}</div>
+          </div>
+        </div>
+        <div className="print-footer">
+          <div>涓荤锛�</div>
+          <div>璁拌处锛�</div>
+          <div>瀹℃牳锛�</div>
+          <div>鍑虹撼锛�</div>
+          <div>鍒跺崟锛�</div>
+        </div>
+      </div>))}
+    </>
+  }
+
+  getTotalLine = (data) => {
+    let totalLine = {uuid: 'total', type: 'total'}
+    let debit = ''
+    let credit = ''
+
+    data.forEach(item => {
+      if (!isNaN(item.debit) && item.debit !== '') {
+        if (debit === '') {
+          debit = 0
+        }
+
+        debit += item.debit
+      } else if (!isNaN(item.credit) && item.credit !== '') {
+        if (debit === '') {
+          debit = 0
+        }
+        if (credit === '') {
+          credit = 0
+        }
+        credit += item.credit
+      }
+    })
+
+    totalLine.debit = debit
+    totalLine.credit = credit
+
+    totalLine.subject_voucher_text = this.changeMoneyToChinese(debit)
+    
+    return totalLine
+  }
+  
+  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 = ''  // 姝h礋鍊兼爣璁�
+
+    if (money === '') return ''
+
+    if (money >= maxNum) return '瓒呭嚭鏈�澶у鐞嗘暟瀛�'
+
+    if (money === 0) {
+      ChineseStr = cnNums[0] + cnIntLast + cnInteger;
+      return ChineseStr
+    }
+    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
+  }
+
+  render() {
+    const { disabled, ID } = this.props
+    const { loading } = this.state
+
+    return (
+      <>
+        <Button className="print-background header-btn" loading={loading} disabled={disabled} onClick={this.trigger}>鎵撳嵃</Button>
+        {loading ? <div id={ID} className="mk-voucher-print-wrap">
+          {this.getContent()}
+        </div> : null}
+      </>
+    )
+  }
+}
+
+export default printVoucher
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/printVoucher/index.scss b/src/tabviews/custom/components/module/voucher/printVoucher/index.scss
new file mode 100644
index 0000000..4ad7c24
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/printVoucher/index.scss
@@ -0,0 +1,126 @@
+.mk-voucher-print-wrap {
+  position: absolute;
+  width: 0;
+  height: 0;
+  overflow: hidden;
+}
+
+.mk-voucher-print-pdf-wrap {
+  width: 1200px;
+  padding: 80px;
+  color: #000000;
+  background: #ffffff;
+  .print-header {
+    .line {
+      display: flex;
+      margin-bottom: 10px;
+
+      div {
+        flex: 1;
+        width: 33.33%;
+        min-height: 5px;
+      }
+      .center {
+        text-align: center;
+      }
+      .right {
+        text-align: right;
+      }
+    }
+    .line:first-child {
+      .center {
+        font-size: 24px;
+      }
+      .right {
+        padding-top: 15px;
+      }
+    }
+  }
+  .print-footer {
+    display: flex;
+
+    div {
+      flex: 1;
+      width: 20%;
+    }
+  }
+  .print-body {
+    position: relative;
+    border: 1px solid #000000;
+    margin: 20px 0px;
+
+    .print-line {
+      position: relative;
+      height: 60px;
+      .remark {
+        display: inline-block;
+        width: 25%;
+        padding: 0 3px;
+      }
+      .subject {
+        display: inline-block;
+        width: 35%;
+        padding: 0 3px;
+      }
+      .credit {
+        display: inline-block;
+        width: 20%;
+        line-height: 60px;
+        text-align: right;
+        padding: 0 3px;
+      }
+
+      .remark::after {
+        content: '';
+        position: absolute;
+        width: 0px;
+        height: 60px;
+        left: 25%;
+        top: 0;
+        border-right: 1px solid #000000;
+      }
+    }
+    .print-line:first-child {
+      > div {
+        text-align: center;
+      }
+    }
+    .print-line:last-child {
+      .remark {
+        width: 60%;
+      }
+      .remark::after {
+        display: none;
+      }
+    }
+    .print-line:not(:last-child)::after {
+      // border: 1px solid #000000;
+      content: '';
+      position: absolute;
+      width: 100%;
+      height: 0px;
+      left: 0;
+      bottom: 0;
+      border-bottom: 1px solid #000000;
+    }
+  }
+
+  .print-body::before {
+    content: '';
+    position: absolute;
+    width: 0px;
+    height: 100%;
+    left: 60%;
+    top: 0;
+    border-right: 1px solid #000000;
+  }
+  .print-body:after {
+    content: '';
+    position: absolute;
+    width: 0px;
+    height: 100%;
+    left: 80%;
+    top: 0;
+    border-right: 1px solid #000000;
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.jsx b/src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.jsx
new file mode 100644
index 0000000..627a699
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.jsx
@@ -0,0 +1,266 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import moment from 'moment'
+import { Upload, Button, Progress, notification } from 'antd'
+import { UploadOutlined } from '@ant-design/icons'
+import SparkMD5 from 'spark-md5'
+import Api from '@/api'
+import './index.scss'
+
+class FileUpload extends Component {
+  static propTpyes = {
+    config: PropTypes.object,  // 琛ㄥ崟淇℃伅
+    onChange: PropTypes.func,  // 琛ㄥ崟鍙樺寲
+  }
+
+  state = {
+    percent: 0,
+    accept: '',
+    accepts: null,
+    maxFile: null,
+    showprogress: false,
+    maxSize: 0,
+    filelist: []
+  }
+
+  UNSAFE_componentWillMount () {
+    const { config } = this.props
+
+    let filelist = []
+    let accept = ''
+    let accepts = null
+
+    this.setState({
+      accept,
+      accepts,
+      filelist,
+      maxSize: config.maxSize || 0,
+      maxFile: config.maxfile
+    })
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  onChange = ({ fileList }) => {
+    fileList = fileList.map(item => {
+      if (item.status === 'error' && /^<!DOCTYPE html>/.test(item.response)) {
+        item.response = ''
+      }
+      return item
+    })
+
+    this.setState({filelist: fileList})
+  }
+
+  onRemove = file => {
+    this.setState({filelist: []})
+
+    this.props.onChange('')
+  }
+
+  onUpdate = (url, name) => {
+    let filelist = fromJS(this.state.filelist).toJS()
+
+    if (filelist[0] && url) {
+      filelist[0].status = 'done'
+      filelist[0].response = url
+    }
+
+    this.setState({filelist})
+    this.props.onChange(url, name)
+  }
+
+  onFail = (msg) => {
+    let filelist = this.state.filelist.map(item => {
+      if (!item.url && !item.response && !item.status) {
+        item.status = 'error'
+      }
+      return item
+    })
+
+    this.setState({filelist, showprogress: false, percent: 0})
+
+    notification.warning({
+      top: 92,
+      message: msg || '鏂囦欢涓婁紶澶辫触锛�',
+      duration: 5
+    })
+  }
+
+  shardupload = (param, name) => {
+    let form = new FormData()
+
+    form.append('file', param.binary)
+    form.append('fileMd5', param.fileMd5)
+    form.append('shardingMd5', param.fileMd5)
+    form.append('baseDomain', window.GLOB.baseurl)
+    form.append('rootPath', 'Content/images/upload/')
+    form.append('fileName', param.fileName)
+    form.append('fileExt', param.fileType)
+    form.append('shardingCnt', 1)
+    form.append('shardingNo', 1)
+    form.append('LoginUID', sessionStorage.getItem('LoginUID') || '')
+    form.append('UserID', sessionStorage.getItem('UserID') || '')
+
+    Api.getLargeFileUpload(form).then(res => {
+      if (res.status) {
+        if (res.urlPath) {
+          this.onUpdate(res.urlPath, name)
+        } else {
+          this.onFail()
+        }
+        this.setState({
+          percent: 100
+        }, () => {
+          setTimeout(() => {
+            this.setState({
+              showprogress: false,
+              percent: 0
+            })
+          }, 200)
+        })
+      } else {
+        this.onFail(res.message)
+      }
+    })
+  }
+
+  getuuid = () => {
+    let uuid = []
+    let _options = '0123456789abcdefghigklmnopqrstuv'
+    for (let i = 0; i < 19; i++) {
+      uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1))
+    }
+    uuid = uuid.join('')
+    return uuid
+  }
+
+  beforeUpload = (file) => {
+    const { accepts, maxSize } = this.state
+
+    if (accepts && file.name) {
+      let pass = false
+      accepts.forEach(type => {
+        if (new RegExp(type + '$', 'ig').test(file.name)) {
+          pass = true
+        }
+      })
+      
+      if (!pass) {
+        setTimeout(() => {
+          this.onFail('鏂囦欢鏍煎紡閿欒锛�')
+        }, 10)
+        return false
+      }
+    }
+    if (maxSize) {
+      let fileSize = file.size / 1024 / 1024
+
+      if (fileSize > maxSize) {
+        setTimeout(() => {
+          this.onFail(`鏂囦欢澶у皬涓嶅彲瓒呰繃${maxSize}M`)
+        }, 10)
+        return false
+      }
+    }
+
+    this.setState({
+      showprogress: true,
+      percent: 0
+    })
+
+    // 鍏煎鎬х殑澶勭悊
+    let spark = new SparkMD5.ArrayBuffer()         // 瀵筧rrayBuffer鏁版嵁杩涜md5鍔犲瘑锛屼骇鐢熶竴涓猰d5瀛楃涓�
+    let totalFileReader = new FileReader()         // 鐢ㄤ簬璁$畻鍑烘�绘枃浠剁殑fileMd5
+    let param = {}
+
+    param.fileName = file.name.replace(/\.{1}[^.]*$/ig, '')  // 鏂囦欢鍚嶏紙鍘婚櫎鍚庣紑鍚嶏級
+    param.fileType = file.name.replace(/^.*\.{1}/ig, '')     // 鏂囦欢绫诲瀷
+
+    if (!/^[A-Za-z0-9]+$/.test(param.fileName)) {            // 鏂囦欢鍚嶇О鍚湁鑻辨枃鍙婃暟瀛椾箣澶栧瓧绗︽椂锛屽悕绉扮郴缁熺敓鎴�
+      param.fileName = moment().format('YYYYMMDDHHmmss') + this.getuuid()
+    }
+
+    totalFileReader.readAsArrayBuffer(file)
+    totalFileReader.onload = (e) => {   // 瀵规暣涓猼otalFile鐢熸垚md5
+      spark.append(e.target.result)
+      param.fileMd5 = spark.end()       // 璁$畻鏁翠釜鏂囦欢鐨刦ileMd5
+      param.binary = file
+
+      let _param = new FormData()
+      _param.append('fileMd5', param.fileMd5)
+      
+      Api.getFilePreUpload(_param).then(res => {
+        if (res.status && res.urlPath) {
+          this.onUpdate(res.urlPath, file.name)
+          this.setState({
+            percent: 100
+          }, () => {
+            setTimeout(() => {
+              this.setState({
+                showprogress: false,
+                percent: 0
+              })
+            }, 200)
+          })
+        } else {
+          this.shardupload(param, file.name)
+        }
+      })
+    }
+
+    totalFileReader.onerror = () => {
+      this.onFail('鏂囦欢璇诲彇澶辫触锛�')
+    }
+
+    return false
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  render() {
+    const { showprogress, percent, filelist, accept } = this.state
+
+    let uploadable = 'attach-form-container '
+
+    if (filelist.length >= 1) {
+      uploadable += 'limit-fileupload'
+    }
+
+    const props = {
+      name: 'file',
+      disabled: showprogress,
+      listType: 'text',
+      fileList: filelist,
+      action: null,
+      accept: accept,
+      method: 'post',
+      multiple: false,
+      onChange: this.onChange,
+      onRemove: this.onRemove,
+      beforeUpload: this.beforeUpload,
+      className: uploadable
+    }
+
+    return (
+      <Upload {...props}>
+        <Button>
+          <UploadOutlined /> 鐐瑰嚮涓婁紶
+        </Button>
+        {showprogress ? <Progress percent={percent} size="small" /> : null}
+      </Upload>
+    )
+  }
+}
+
+export default FileUpload
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.scss b/src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.scss
new file mode 100644
index 0000000..c49de9f
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.scss
@@ -0,0 +1,29 @@
+.attach-form-container {
+  .ant-progress-small.ant-progress-line {
+    position: absolute;
+    bottom: -20px;
+    left: 0px;
+  }
+  a[href^="data"] {
+    pointer-events: none;
+    .anticon-eye-o {
+      display: none;
+    }
+  }
+}
+.attach-form-container.limit-fileupload {
+  > .ant-upload {
+    display: inline;
+    >.ant-upload {
+      >input {
+        display: none;
+      }
+      >button {
+        display: none;
+      }
+    }
+  }
+  > .ant-upload-select-picture-card {
+    display: none;
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/resetAttach/addAttach/index.jsx b/src/tabviews/custom/components/module/voucher/resetAttach/addAttach/index.jsx
new file mode 100644
index 0000000..a47a6e2
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/resetAttach/addAttach/index.jsx
@@ -0,0 +1,160 @@
+import React, {Component} from 'react'
+import { Form, Input, Select, Radio, Col } from 'antd'
+
+import MKFileUpload from './fileupload'
+
+const { TextArea } = Input
+
+class AddAttach extends Component {
+  state = {
+    f_method: 'upload',
+  }
+
+  handleConfirm = () => {
+    const { files } = this.props
+
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (err) return
+
+        files.forEach(item => {
+          if (item.data_code === values.data_code) {
+            values.data_name = item.data_name
+            values.BID = item.id
+          }
+        })
+
+        if (values.f_method === 'input') {
+          values.url = values.fileurl
+        }
+
+        resolve(values)
+      })
+    })
+  }
+
+  fileChange = (val, name) => {
+    if (name) {
+      this.props.form.setFieldsValue({attachments_title: name})
+    }
+  }
+
+  render() {
+    const { files } = this.props
+    const { getFieldDecorator } = this.props.form
+    const { f_method } = this.state
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 6 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout} onKeyDown={this.onEnterSubmit}>
+        <Col span={12}>
+          <Form.Item label="鏂囦欢澶�">
+            {getFieldDecorator('data_code', {
+              initialValue: '',
+              rules: [
+                {
+                  required: true,
+                  message: '璇烽�夋嫨鏂囦欢澶癸紒'
+                }
+              ]
+            })(<Select>
+              {files.map(option =>
+                <Select.Option key={option.id} value={option.data_code}>{option.data_name}</Select.Option>
+              )}
+            </Select>)}
+          </Form.Item>
+        </Col>
+        <Col span={12}>
+          <Form.Item label="娣诲姞鏂瑰紡">
+            {getFieldDecorator('f_method', {
+              initialValue: 'upload',
+              rules: [
+                {
+                  required: true,
+                  message: '璇烽�夋嫨娣诲姞鏂瑰紡锛�'
+                }
+              ]
+            })(<Radio.Group onChange={(e) => this.setState({f_method: e.target.value})}>
+                <Radio value="upload">涓婁紶</Radio>
+                <Radio value="input">杈撳叆</Radio>
+              </Radio.Group>)}
+          </Form.Item>
+        </Col>
+        {f_method === 'upload' ? <Col span={12}>
+          <Form.Item label="鏂囦欢">
+            {getFieldDecorator('url', {
+              initialValue: '',
+              rules: [
+                {
+                  required: true,
+                  message: '璇锋坊鍔犳枃浠讹紒'
+                }
+              ]
+            })(<MKFileUpload config={{
+                initval: '',
+                compress: 'false',
+                suffix: '',
+                maxfile: 1,
+                fileType: 'text'
+              }} onChange={(val, name) => this.fileChange(val, name)} />)}
+          </Form.Item>
+        </Col> : <Col span={24}>
+          <Form.Item label="鏂囦欢鍦板潃">
+            {getFieldDecorator('fileurl', {
+              initialValue: '',
+              rules: [
+                {
+                  required: true,
+                  message: '璇疯緭鍏ユ枃浠跺湴鍧�锛�'
+                }
+              ]
+            })(<TextArea autoSize={{ minRows: 2 }}/>)}
+          </Form.Item>
+        </Col>}
+        <Col span={12}>
+          <Form.Item label="鏂囦欢鍚�">
+            {getFieldDecorator('attachments_title', {
+              initialValue: '',
+              rules: [
+                {
+                  required: true,
+                  message: '璇疯緭鍏ユ枃浠跺悕锛�'
+                },
+                {
+                  max: 50,
+                  message: '鏈�澶ч暱搴︿负50浣嶏紒'
+                }
+              ]
+            })(<Input />)}
+          </Form.Item>
+        </Col>
+        <Col span={24}>
+          <Form.Item label="澶囨敞">
+            {getFieldDecorator('remark', {
+              initialValue: '',
+              rules: [
+                {
+                  max: 512,
+                  message: '鏈�澶ч暱搴︿负512浣嶏紒'
+                }
+              ]
+            })(<TextArea autoSize={{ minRows: 2 }}/>)}
+          </Form.Item>
+        </Col>
+        <div style={{clear: 'both'}}></div>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(AddAttach)
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/resetAttach/documents/index.jsx b/src/tabviews/custom/components/module/voucher/resetAttach/documents/index.jsx
new file mode 100644
index 0000000..ea36b1c
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/resetAttach/documents/index.jsx
@@ -0,0 +1,120 @@
+import React, {Component} from 'react'
+
+class Documents extends Component {
+  state = {
+    selectKey: [],
+    actFolder: null
+  }
+
+  UNSAFE_componentWillMount() {
+    const { documents } = this.props
+
+    let actFolder = documents[0] || null
+
+    this.setState({selectKey: [], actFolder: actFolder})
+  }
+
+  submit = () => {
+    const { selectKey, actFolder } = this.state
+
+    let items = []
+
+    if (actFolder && selectKey.length > 0) {
+      actFolder.attachments.forEach(doc => {
+        if (selectKey.includes(doc.id)) {
+          items.push(doc)
+        }
+      })
+    }
+
+    this.props.onChange(items)
+  }
+
+  checkfolder = (id) => {
+    const { documents } = this.props
+    const { actFolder } = this.state
+
+    if (!actFolder || actFolder.id === id) return
+
+    let _actFolder = null
+    documents.forEach(item => {
+      if (item.id === id) {
+        _actFolder = item
+      }
+    })
+
+    this.setState({actFolder: _actFolder, selectKey: []}, () => {
+      this.submit()
+    })
+  }
+
+  checkItem = (id) => {
+    const { selectKey } = this.state
+
+    if (selectKey.includes(id)) {
+      this.setState({selectKey: selectKey.filter(key => key !== id)}, () => {
+        this.submit()
+      })
+    } else {
+      this.setState({selectKey: [...selectKey, id]}, () => {
+        this.submit()
+      })
+    }
+  }
+
+  checkAllItem = () => {
+    const { selectKey, actFolder } = this.state
+
+    if (!actFolder || actFolder.attachments.length === 0) return
+
+    if (selectKey.length === actFolder.attachments.length) {
+      this.setState({selectKey: []}, () => {
+        this.submit()
+      })
+    } else {
+      this.setState({selectKey: actFolder.attachments.map(item => item.id)}, () => {
+        this.submit()
+      })
+    }
+  }
+
+  render() {
+    const { documents } = this.props
+    const { selectKey, actFolder } = this.state
+
+    let checkAll = ''
+    if (actFolder && selectKey.length > 0) {
+      checkAll = selectKey.length < actFolder.attachments.length ? ' half' : ' active'
+    } else if (!actFolder || actFolder.attachments.length === 0) {
+      checkAll = ' disabled'
+    }
+
+    return (
+      <div className="document-wrap">
+        <div className="document-title">
+          <div className="folder-box">鏂囦欢澶�</div>
+          <div className="folder">
+            <span onClick={this.checkAllItem} className={'square-select' + checkAll}></span>
+            <span>鏂囦欢</span>
+          </div>
+        </div>
+        <div className="document-body">
+          <div className="doc-name">
+            {documents.map(folder => (<div className={actFolder.id === folder.id ? 'active' : ''} onClick={() => this.checkfolder(folder.id)} key={folder.id}>{folder.data_name}</div>))}
+          </div>
+          <div className="file-wrap">
+            {actFolder && actFolder.attachments.map(doc => {
+              return <div className="file-item" key={doc.id}>
+                <span onClick={() => this.checkItem(doc.id)} className={'square-select' + (selectKey.indexOf(doc.id) > -1 ? ' active' : '')}></span>
+                <img src={doc.icon} alt=""/>
+                <span className="file-name">{doc.attachments_title}</span>
+              </div>
+            })}
+          </div>
+        </div>
+      </div>
+    )
+  }
+}
+
+export default Documents
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/resetAttach/documents/index.scss b/src/tabviews/custom/components/module/voucher/resetAttach/documents/index.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/resetAttach/documents/index.scss
diff --git a/src/tabviews/custom/components/module/voucher/resetAttach/index.jsx b/src/tabviews/custom/components/module/voucher/resetAttach/index.jsx
new file mode 100644
index 0000000..fcbcb81
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/resetAttach/index.jsx
@@ -0,0 +1,297 @@
+import React, {Component} from 'react'
+import { fromJS } from 'immutable'
+import { Button, Modal, notification, Spin } from 'antd'
+import moment from 'moment'
+import { DeleteOutlined } from '@ant-design/icons'
+// import md5 from 'md5'
+
+import Api from '@/api'
+import AddAttach from './addAttach'
+import Documents from './documents'
+import './index.scss'
+
+import wordImg from '@/assets/img/file-word-fill.png'
+import excelImg from '@/assets/img/file-excel-fill.png'
+import fileImg from '@/assets/img/file-fill.png'
+import pdfImg from '@/assets/img/file-pdf-fill.png'
+import pptImg from '@/assets/img/file-ppt-fill.png'
+import picImg from '@/assets/img/picture-fill.png'
+import rarImg from '@/assets/img/rar.png'
+
+class ResetAttach extends Component {
+  state = {
+    visible: false,
+    upVisible: false,
+    docVisible: false,
+    files: [],
+    list: [],
+    documents: [],
+    selectDocs: [],
+    loading: false
+  }
+
+  submit = () => {
+    this.setState({visible: false})
+    this.props.onChange(this.state.list)
+  }
+
+  trigger = () => {
+    const { voucherCode, orgcode } = this.props
+    let list = fromJS(this.props.attachlist).toJS()
+
+    list = list.map(item => {
+      item.icon = this.getIcon(item.attachments_url)
+      return item
+    })
+
+    this.setState({visible: true, loading: true, list: list, files: [], documents: []})
+
+    let param = {
+      func: 's_get_fcc_voucher_attachments',
+      voucher_code: voucherCode || '',
+      orgcode: orgcode
+    }
+
+    Api.genericInterface(param).then(result => {
+      this.setState({loading: false})
+
+      if (!result.status) {
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+        return
+      }
+
+      if (result.fcc_files) {
+        let files = []
+        let documents = []
+
+        result.fcc_files.forEach(file => {
+          files.push({data_code: file.data_code, data_name: file.data_name, id: file.id})
+
+          file.attachments = file.attachments.map(item => {
+            item.id = item.attach_id
+            item.icon = this.getIcon(item.attachments_url)
+            item.data_code = file.data_code
+            item.data_name = file.data_name
+            item.BID = file.id
+
+            return item
+          })
+
+          documents.push(file)
+        })
+
+        this.setState({files, documents})
+      }
+    })
+  }
+
+  upSubmit = () => {
+    const { config, orgcode } = this.props
+
+    if (!orgcode) {
+      notification.warning({
+        top: 92,
+        message: '鍏徃缂栫爜涓嶅彲涓虹┖锛�',
+        duration: 5
+      })
+      return
+    }
+
+    let ID = (() => {
+      let uuid = []
+      let options = '0123456789abcdefghigklmnopqrstuv'
+      for (let i = 0; i < 19; i++) {
+        uuid.push(options.substr(Math.floor(Math.random() * 0x20), 1))
+      }
+      uuid = moment().format('YYYYMMDDHHmmssSSS') + uuid.join('')
+      return uuid.toUpperCase()
+    })()
+
+    this.formRef.handleConfirm().then(res => {
+      let param = {
+        func: 's_fcc_voucher_attachments_addupt',
+        data_code: res.data_code,
+        data_name: res.data_name,
+        id: ID,
+        orgcode: orgcode,
+        voucher_at_lp: '',
+        attachments_title: res.attachments_title,
+        f_method: res.f_method,
+        attachments_url: res.url,
+        remark: res.remark,
+        status: config.wrap.attachStatus !== 10 ? 0 : 10,
+        statusname: config.wrap.attachStatus !== 10 ? '寰呭鏍�' : '宸插鏍�',
+        typename: config.name,
+        UserName: sessionStorage.getItem('User_Name') || '',
+        FullName: sessionStorage.getItem('Full_Name') || '',
+        BID: res.BID
+      }
+
+      Api.genericInterface(param).then(result => {
+        if (!result.status) {
+          notification.warning({
+            top: 92,
+            message: result.message,
+            duration: 5
+          })
+          return
+        }
+
+        let list = fromJS(this.state.list).toJS()
+        let item = {
+          id: ID,
+          data_code: res.data_code,
+          data_name: res.data_name,
+          attachments_title: res.attachments_title,
+          attachments_url: res.url,
+          BID: res.BID
+        }
+
+        item.icon = this.getIcon(res.url)
+
+        list.push(item)
+
+        this.setState({list: list, upVisible: false})
+      })
+    })
+  }
+
+  deleteFile = (id) => {
+    this.setState({list: this.state.list.filter(item => item.id !== id)})
+  }
+
+  getIcon = (url) => {
+    let type = 'file'
+
+    if (/(.png|.jpg|.gif|.jpeg)$/i.test(url)) {
+      type = 'pic'
+    } else if (/(.doc|.docx)$/i.test(url)) {
+      type = 'word'
+    } else if (/(.xls|.xlsx)$/i.test(url)) {
+      type = 'excel'
+    } else if (/(.zip|.rar)$/i.test(url)) {
+      type = 'rar'
+    } else if (/.pdf$/i.test(url)) {
+      type = 'pdf'
+    } else if (/.pptx$/i.test(url)) {
+      type = 'ppt'
+    }
+
+    let icon = fileImg
+    if (type === 'excel') {
+      icon = excelImg
+    } else if (type === 'word') {
+      icon = wordImg
+    } else if (type === 'pdf') {
+      icon = pdfImg
+    } else if (type === 'pic') {
+      icon = picImg
+    } else if (type === 'ppt') {
+      icon = pptImg
+    } else if (type === 'rar') {
+      icon = rarImg
+    }
+
+    return icon
+  }
+
+  docSubmit = () => {
+    const { selectDocs, list } = this.state
+
+    
+    if (selectDocs.length > 0 && list.length > 0) {
+      let _list = fromJS(list).toJS()
+      let err = ''
+      let ids = list.map(item => item.id)
+
+      selectDocs.forEach(item => {
+        if (!ids.includes(item.id)) {
+          _list.push(item)
+        } else {
+          err += (err ? '銆�' : '') + item.attachments_title
+        }
+      })
+
+      this.setState({list: _list, docVisible: false})
+
+      if (err) {
+        notification.warning({
+          top: 92,
+          message: '鏂囦欢' + err + '宸叉坊鍔犮��',
+          duration: 5
+        })
+      }
+    } else {
+      this.setState({list: [...list, ...selectDocs], docVisible: false})
+    }
+  }
+
+  render() {
+    const { visible, upVisible, docVisible, files, list, documents, loading } = this.state
+
+    return (
+      <>
+        <Button type="link" onClick={this.trigger}>闄勪欢</Button>
+        <Modal
+          title="娣诲姞闄勪欢"
+          wrapClassName="voucher-attach-wrap"
+          visible={visible}
+          width={700}
+          maskClosable={false}
+          onOk={this.submit}
+          onCancel={() => { this.setState({ visible: false })}}
+          cancelText=""
+          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>
+          <div className="attach-selected-list">
+            {list.map(item => {
+              return <div className="attach-item" key={item.id}>
+                <img src={item.icon} alt=""/>
+                <div className="attach-msg">
+                  <div>{item.attachments_title}</div>
+                  <div>{item.data_name ? item.data_name + ' / ' : ''}{item.attachments_url}</div>
+                </div>
+                <div>
+                  <DeleteOutlined onClick={() => this.deleteFile(item.id)}/>
+                </div>
+              </div>
+            })}
+          </div>
+        </Modal>
+        <Modal
+          title="娣诲姞闄勪欢"
+          wrapClassName="voucher-attach-add-wrap"
+          visible={upVisible}
+          width={700}
+          maskClosable={false}
+          onOk={this.upSubmit}
+          onCancel={() => { this.setState({ upVisible: false })}}
+          destroyOnClose
+        >
+          <AddAttach files={files} wrappedComponentRef={(inst) => this.formRef = inst} submit={this.upSubmit}/>
+        </Modal>
+        <Modal
+          title="鐢靛瓙妗f"
+          wrapClassName="voucher-attach-document-wrap"
+          visible={docVisible}
+          width={700}
+          maskClosable={false}
+          onOk={this.docSubmit}
+          onCancel={() => { this.setState({ docVisible: false, selectDocs: [] })}}
+          destroyOnClose
+        >
+          {docVisible ? <Documents documents={documents} onChange={(vals) => this.setState({selectDocs: vals})}/> : null}
+        </Modal>
+      </>
+    )
+  }
+}
+
+export default ResetAttach
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/resetAttach/index.scss b/src/tabviews/custom/components/module/voucher/resetAttach/index.scss
new file mode 100644
index 0000000..94b8bcf
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/resetAttach/index.scss
@@ -0,0 +1,235 @@
+.voucher-attach-wrap {
+  .ant-modal-body {
+    position: relative;
+    padding: 15px;
+    .ant-spin {
+      position: absolute;
+      top: 150px;
+      left: 350px;
+    }
+
+    .attach-type-btn {
+      border-width: 1px;
+      color: var(--mk-sys-color);
+      border-color: var(--mk-sys-color);
+      margin-right: 15px;
+    }
+
+    .attach-selected-list {
+      height: 300px;
+      overflow-y: auto;
+      border: 1px solid #d8d8d8;
+      margin-top: 10px;
+
+      .attach-item {
+        display: flex;
+        border-bottom: 1px solid #f1f1f1;
+        img {
+          width: 45px;
+          height: 45px;
+          padding: 9px;
+          margin-top: 5px;
+        }
+        .attach-msg {
+          flex: 1;
+          width: calc(100% - 95px);
+          padding: 5px 0px;
+          div {
+            color: #000000;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+          }
+        }
+        .attach-msg + div {
+          width: 50px;
+          line-height: 45px;
+          margin-top: 5px;
+          text-align: center;
+          font-size: 16px;
+          cursor: pointer;
+          color: orangered;
+        }
+      }
+    }
+    .attach-selected-list::-webkit-scrollbar {
+      width: 7px;
+    }
+    .attach-selected-list::-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);
+    }
+    .attach-selected-list::-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);
+    }
+  }
+}
+.voucher-attach-add-wrap {
+  .ant-form {
+    > .ant-col {
+      float: none;
+      display: inline-block;
+      vertical-align: top;
+    }
+    > .ant-col-24 {
+      .ant-form-item-label {
+        width: 12.5%;
+      }
+      .ant-form-item-control-wrapper {
+        width: 84%;
+      }
+    }
+  }
+}
+.voucher-attach-document-wrap {
+  .ant-modal-body {
+    padding: 15px;
+  }
+  .document-wrap {
+    .document-title {
+      display: flex;
+      background: #e8e8e8;
+      line-height: 38px;
+      border: 1px solid #d8d8d8;
+      border-bottom: none;
+      .folder-box {
+        width: 200px;
+        text-align: center;
+        border-right: 1px solid #d8d8d8;
+      }
+      .folder {
+        flex: 1;
+        .square-select {
+          top: 3px;
+          margin: 0 10px;
+        }
+      }
+    }
+    .document-body {
+      border: 1px solid #d8d8d8;
+      height: 300px;
+      display: flex;
+      .doc-name {
+        width: 200px;
+        text-align: center;
+        border-right: 1px solid #d8d8d8;
+        height: 100%;
+        overflow-y: auto;
+        div {
+          border-bottom: 1px solid #e8e8e8;
+          height: 40px;
+          line-height: 40px;
+        }
+        .active {
+          background: var(--mk-sys-color1);
+        }
+      }
+      .file-wrap {
+        flex: 1;
+        width: calc(100% - 200px);
+        height: 100%;
+        overflow-y: auto;
+        .file-item {
+          border-bottom: 1px solid #e8e8e8;
+          height: 40px;
+          line-height: 40px;
+
+          .square-select {
+            top: 4px;
+            margin: 0 10px;
+          }
+          img {
+            width: 20px;
+            height: 20px;
+            margin-top: -2px;
+            margin-right: 5px;
+          }
+          .file-name {
+            display: inline-block;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            vertical-align: top;
+            width: calc(100% - 90px);
+          }
+        }
+      }
+    }
+
+    .doc-name::-webkit-scrollbar, .file-wrap::-webkit-scrollbar {
+      width: 7px;
+    }
+    .doc-name::-webkit-scrollbar-thumb, .file-wrap::-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);
+    }
+    .doc-name::-webkit-scrollbar-track, .file-wrap::-webkit-scrollbar-track {
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      border: 1px solid rgba(0, 0, 0, 0.07);
+      background: rgba(0, 0, 0, 0);
+    }
+
+    .square-select {
+      position: relative;
+      display: inline-block;
+      width: 16px;
+      height: 16px;
+      border: 1px solid #cccccc;
+      box-sizing: content-box;
+      margin: auto;
+      margin-right: 5px;
+      background-color: #ffffff;
+      transition: border-color 0.2s;
+      cursor: pointer;
+    }
+
+    .square-select.active {
+      border-color: var(--mk-sys-color);
+      background: var(--mk-sys-color);
+    }
+    .square-select.half {
+      border-color: var(--mk-sys-color);
+    }
+    .square-select.disabled {
+      border-color: #e8e8e8;
+      background: #e8e8e8;
+      cursor: default;
+    }
+    .square-select.disabled::before {
+      border-color: #e8e8e8;
+    }
+    .square-select.half::before {
+      display: none;
+    }
+    .square-select.half::after {
+      position: absolute;
+      top: 4px;
+      left: 4px;
+      content: ' ';
+      display: block;
+      width: 8px;
+      height: 8px;
+      background: var(--mk-sys-color);
+    }
+
+    .square-select::before {
+      position: relative;
+      top: 1px;
+      left: 6px;
+      content: ' ';
+      display: block;
+      width: 5px;
+      height: 11px;
+      border-style: solid;
+      border-width: 0 2px 2px 0;
+      border-color: #ffffff;
+      transform: rotate(45deg);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/resetRemark/index.jsx b/src/tabviews/custom/components/module/voucher/resetRemark/index.jsx
new file mode 100644
index 0000000..a2a28b9
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/resetRemark/index.jsx
@@ -0,0 +1,62 @@
+import React, {Component} from 'react'
+import { Button, Modal, Input, notification } from 'antd'
+
+// import './index.scss'
+
+const { TextArea } = Input
+
+class ResetRemark extends Component {
+  state = {
+    visible: false,
+    remark: ''
+  }
+
+  submit = () => {
+    const { ID } = this.props
+
+    let node = document.getElementById(ID)
+    let val = node.value
+
+    if (val && val.length > 512) {
+      notification.warning({
+        top: 92,
+        message: '褰撳墠鍐呭瓒呴暱锛屽娉ㄦ渶澶�512涓瓧绗︺��',
+        duration: 5
+      })
+      return
+    }
+
+    this.setState({remark: '', visible: false})
+    this.props.onChange(val)
+  }
+
+  trigger = () => {
+    const { remark } = this.props
+
+    this.setState({visible: true, remark: remark})
+  }
+
+  render() {
+    const { ID } = this.props
+    const { visible, remark } = this.state
+
+    return (
+      <>
+        <Button type="link" onClick={this.trigger}>澶囨敞</Button>
+        <Modal
+          title="澶囨敞"
+          visible={visible}
+          width={700}
+          maskClosable={false}
+          onOk={this.submit}
+          onCancel={() => { this.setState({ visible: false })}}
+          destroyOnClose
+        >
+          <TextArea id={ID} defaultValue={remark} rows={6}/>
+        </Modal>
+      </>
+    )
+  }
+}
+
+export default ResetRemark
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/resetRemark/index.scss b/src/tabviews/custom/components/module/voucher/resetRemark/index.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/resetRemark/index.scss
diff --git a/src/tabviews/custom/components/module/voucher/saveAsTemp/index.jsx b/src/tabviews/custom/components/module/voucher/saveAsTemp/index.jsx
new file mode 100644
index 0000000..6c5c151
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/saveAsTemp/index.jsx
@@ -0,0 +1,122 @@
+import React, {Component} from 'react'
+import { Button, Modal, Form, Input, notification, Select, Divider } from 'antd'
+import { PlusOutlined } from '@ant-design/icons'
+
+import MKEmitter from '@/utils/events.js'
+// import './index.scss'
+
+const { Option } = Select
+
+class SaveAsTemp extends Component {
+  state = {
+    visible: false,
+    name: '',
+    typeChar: '',
+    typeName: '',
+    menu: null
+  }
+
+  UNSAFE_componentWillMount() {
+    let menuId = '16289973311406f3ko9nm8ehotdmu80o'
+    let menu = window.GLOB.mkThdMenus.filter(m => m.MenuID === menuId)[0]
+
+    if (menu) {
+      let newtab = {
+        ...menu,
+        param: {}
+      }
+
+      this.setState({menu: newtab})
+    }
+  }
+
+  submit = () => {
+    const { name, typeChar, typeName } = this.state
+
+    if (!typeChar) {
+      notification.warning({
+        top: 92,
+        message: '璇烽�夋嫨妯℃澘绫诲瀷锛�',
+        duration: 5
+      })
+      return
+    } else if (!name) {
+      notification.warning({
+        top: 92,
+        message: '璇峰~鍐欐ā鏉垮悕绉帮紒',
+        duration: 5
+      })
+      return
+    }
+
+    this.setState({visible: false})
+    this.props.onChange(name, typeChar, typeName)
+  }
+
+  triggerSave = () => {
+    this.setState({visible: true, name: '', typeChar: '', typeName: ''})
+  }
+
+  changeType = (val, option) => {
+    if (option && option.props) {
+      this.setState({typeChar: option.props.value, typeName: option.props.children})
+    }
+  }
+
+  addType = () => {
+    const { menu } = this.state
+
+    this.setState({visible: false})
+
+    MKEmitter.emit('modifyTabs', menu, true)
+  }
+
+  render() {
+    const { tempTypes } = this.props
+    const { visible, menu } = this.state
+
+    return (
+      <>
+        <Button onClick={() => this.triggerSave()}>淇濆瓨涓烘ā鏉�</Button>
+        <Modal
+          title="娣诲姞妯℃澘"
+          wrapClassName="mk-temp-add-modal"
+          visible={visible}
+          width={600}
+          maskClosable={false}
+          onOk={this.submit}
+          onCancel={() => { this.setState({ visible: false })}}
+          destroyOnClose
+        >
+          {visible ? <Form>
+            <Form.Item label="妯℃澘绫诲瀷">
+              {menu ? <Select placeholder="璇烽�夋嫨妯℃澘绫诲瀷" onChange={this.changeType} dropdownRender={menus => (
+                <div>
+                  {menus}
+                  <Divider style={{ margin: '4px 0' }} />
+                  <div className="mk-add-book" onMouseDown={this.addType}>
+                    <PlusOutlined /> 娣诲姞
+                  </div>
+                </div>
+              )}>
+                {tempTypes.map(item => (
+                  <Option key={item.data_code} value={item.data_code}>{item.data_name}</Option>
+                ))}
+              </Select> : <Select placeholder="璇烽�夋嫨妯℃澘绫诲瀷" onChange={this.changeType}>
+                {tempTypes.map(item => (
+                  <Option key={item.data_code} value={item.data_code}>{item.data_name}</Option>
+                ))}
+              </Select>}
+            </Form.Item>
+            <Form.Item label="妯℃澘鍚嶇О">
+              <Input onChange={(e) => this.setState({name: e.target.value})}/>
+            </Form.Item>
+            <div style={{clear: 'both'}}></div>
+          </Form> : null}
+        </Modal>
+      </>
+    )
+  }
+}
+
+export default SaveAsTemp
\ No newline at end of file
diff --git a/src/tabviews/custom/components/module/voucher/saveAsTemp/index.scss b/src/tabviews/custom/components/module/voucher/saveAsTemp/index.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tabviews/custom/components/module/voucher/saveAsTemp/index.scss
diff --git a/src/tabviews/custom/components/module/voucher/voucherTable/index.jsx b/src/tabviews/custom/components/module/voucher/voucherTable/index.jsx
index 68aa28d..327a6c9 100644
--- a/src/tabviews/custom/components/module/voucher/voucherTable/index.jsx
+++ b/src/tabviews/custom/components/module/voucher/voucherTable/index.jsx
@@ -748,21 +748,21 @@
             if (record.sup_accounting && record.supAccounts) {
               record.supAccounts.forEach(item => {
                 if (item.sup_acc_type === 'supplier') {
-                  val += item.suppliercode ? '_' + item.suppliercode : ''
+                  val += item.suppliercode ? '_' + item.suppliercode + ' ' + item.suppliername : ''
                 } else if (item.sup_acc_type === 'customer') {
-                  val += item.customercode ? '_' + item.customercode : ''
+                  val += item.customercode ? '_' + item.customercode + ' ' + item.customername : ''
                 } else if (item.sup_acc_type === 'department') {
-                  val += item.co_pro_code ? '_' + item.co_pro_code : ''
+                  val += item.co_pro_code ? '_' + item.co_pro_code + ' ' + item.co_pro_name : ''
                 } else if (item.sup_acc_type === 'project') {
-                  val += item.projectcode ? '_' + item.projectcode : ''
+                  val += item.projectcode ? '_' + item.projectcode + ' ' + item.projectname : ''
                 } else if (item.sup_acc_type === 'inventory') {
-                  val += item.productcode ? '_' + item.productcode : ''
+                  val += item.productcode ? '_' + item.productcode + ' ' + item.productname : ''
                 } else if (item.sup_acc_type === 'employee') {
-                  val += item.workercode ? '_' + item.workercode : ''
+                  val += item.workercode ? '_' + item.workercode + ' ' + item.workername : ''
                 } else if (item.sup_acc_type === 'cash_flow') {
-                  val += item.cash_flow_code ? '_' + item.cash_flow_code : ''
+                  val += item.cash_flow_code ? '_' + item.cash_flow_code + ' ' + item.cash_flow_name : ''
                 } else if (item.sup_acc_code) {
-                  val += '_' + item.sup_acc_code
+                  val += '_' + item.sup_acc_code + ' ' + item.sup_acc_name
                 }
               })
             }
diff --git a/src/tabviews/custom/components/share/tabtransfer/index.jsx b/src/tabviews/custom/components/share/tabtransfer/index.jsx
index aebf282..f7bbac1 100644
--- a/src/tabviews/custom/components/share/tabtransfer/index.jsx
+++ b/src/tabviews/custom/components/share/tabtransfer/index.jsx
@@ -22,6 +22,7 @@
 const AntvScatter = asyncComponent(() => import('@/tabviews/custom/components/chart/antv-scatter'))
 const TableCard = asyncComponent(() => import('@/tabviews/custom/components/card/table-card'))
 const NormalTable = asyncComponent(() => import('@/tabviews/custom/components/table/normal-table'))
+const DoubleDataCard = asyncComponent(() => import('@/tabviews/custom/components/card/double-data-card'))
 const EditTable = asyncComponent(() => import('@/tabviews/custom/components/table/edit-table'))
 const PropCard = asyncComponent(() => import('@/tabviews/custom/components/card/prop-card'))
 const NormalGroup = asyncComponent(() => import('@/tabviews/custom/components/group/normal-group'))
@@ -204,6 +205,12 @@
             <NormalTable config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
+      } else if (item.type === 'card' && item.subtype === 'dualdatacard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <DoubleDataCard config={item} mainSearch={mainSearch}/>
+          </Col>
+        )
       } else if (item.type === 'bar' || item.type === 'line') {
         return (
           <Col span={item.width} style={style} key={item.uuid}>
diff --git a/src/tabviews/custom/components/table/base-table/index.jsx b/src/tabviews/custom/components/table/base-table/index.jsx
index f8f7454..08bd627 100644
--- a/src/tabviews/custom/components/table/base-table/index.jsx
+++ b/src/tabviews/custom/components/table/base-table/index.jsx
@@ -46,7 +46,6 @@
    */
   UNSAFE_componentWillMount () {
     let _config = fromJS(this.props.config).toJS()
-    let _cols = new Map()
 
     let BID = ''
     let BData = ''
@@ -75,11 +74,6 @@
     } else {
       setting.orisel = true
     }
-
-    _config.columns.forEach(item => {
-      if (item.type !== 'number') return
-      _cols.set(item.field, item)
-    })
 
     _config.cols.forEach(column => {
       if (column.type === 'action') {
diff --git a/src/tabviews/custom/components/table/edit-table/index.jsx b/src/tabviews/custom/components/table/edit-table/index.jsx
index 49dae01..c8c762b 100644
--- a/src/tabviews/custom/components/table/edit-table/index.jsx
+++ b/src/tabviews/custom/components/table/edit-table/index.jsx
@@ -48,7 +48,6 @@
    */
   UNSAFE_componentWillMount () {
     let _config = fromJS(this.props.config).toJS()
-    let _cols = new Map()
     let setting = {..._config.setting, ..._config.wrap}
     setting.tableId = Utils.getuuid()
 
@@ -82,11 +81,6 @@
       setting.operType = 'btnMode'
     }
 
-    _config.columns.forEach(item => {
-      if (item.type !== 'number') return
-      _cols.set(item.field, item)
-    })
-
     let _columns = []
     let signAdd = false
     _config.cols.forEach(column => {
@@ -117,14 +111,7 @@
         })
       }
 
-      if (column.type === 'custom') {
-        column.elements = column.elements.map(item => {
-          if (item.eleType === 'number' && item.field && _cols.has(item.field) && typeof(item.decimal) !== 'number') {
-            item.decimal = _cols.get(item.field).decimal || 0
-          }
-          return item
-        })
-      } else if (column.type === 'action') {
+      if (column.type === 'action') {
         column.operations = column.elements
       }
 
diff --git a/src/tabviews/custom/components/table/normal-table/index.jsx b/src/tabviews/custom/components/table/normal-table/index.jsx
index 0ffafaa..efacce5 100644
--- a/src/tabviews/custom/components/table/normal-table/index.jsx
+++ b/src/tabviews/custom/components/table/normal-table/index.jsx
@@ -54,7 +54,6 @@
   UNSAFE_componentWillMount () {
     const { data, initdata } = this.props
     let _config = fromJS(this.props.config).toJS()
-    let _cols = new Map()
     let _data = null
     let _sync = _config.setting.sync === 'true'
 
@@ -131,20 +130,8 @@
       }
     }
 
-    _config.columns.forEach(item => {
-      if (item.type !== 'number') return
-      _cols.set(item.field, item)
-    })
-
     _config.cols.forEach(column => {
-      if (column.type === 'custom') {
-        column.elements = column.elements.map(item => {
-          if (item.eleType === 'number' && item.field && _cols.has(item.field) && typeof(item.decimal) !== 'number') {
-            item.decimal = _cols.get(item.field).decimal || 0
-          }
-          return item
-        })
-      } else if (column.type === 'action') {
+      if (column.type === 'action') {
         column.operations = column.elements
       }
     })
diff --git a/src/tabviews/custom/components/timeline/normal-timeline/index.jsx b/src/tabviews/custom/components/timeline/normal-timeline/index.jsx
index d8ed53e..2d9f4c7 100644
--- a/src/tabviews/custom/components/timeline/normal-timeline/index.jsx
+++ b/src/tabviews/custom/components/timeline/normal-timeline/index.jsx
@@ -41,7 +41,6 @@
   UNSAFE_componentWillMount () {
     const { data, initdata } = this.props
     let _config = fromJS(this.props.config).toJS()
-    let _cols = new Map()
 
     let _data = null
     let card = null
@@ -82,18 +81,7 @@
     _config.search = []
     _config.wrap.contentHeight = _config.wrap.title ? 'calc(100% - 45px)' : '100%'
 
-    _config.columns.forEach(item => {
-      if (item.type !== 'number') return
-      _cols.set(item.field, item)
-    })
-
     card = _config.subcards[0]
-    card.elements = card.elements.map(item => {
-      if (item.eleType === 'number' && item.field && _cols.has(item.field) && typeof(item.decimal) !== 'number') {
-        item.decimal = _cols.get(item.field).decimal || 0
-      }
-      return item
-    })
 
     this.setState({
       card,
@@ -230,7 +218,7 @@
     this.loadData()
   }
 
-   /**
+  /**
    * @description 瀵煎嚭Excel鏃讹紝鑾峰彇椤甸潰鎼滅储鎺掑簭绛夊弬鏁�
    */
   queryModuleParam = (menuId, callback) => {
diff --git a/src/tabviews/custom/components/tree/antd-tree/index.jsx b/src/tabviews/custom/components/tree/antd-tree/index.jsx
index 688fe7a..8755abb 100644
--- a/src/tabviews/custom/components/tree/antd-tree/index.jsx
+++ b/src/tabviews/custom/components/tree/antd-tree/index.jsx
@@ -1,17 +1,20 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, Empty, notification, Input, Tree, Modal } from 'antd'
-import { FolderOpenOutlined, FolderOutlined, FileOutlined } from '@ant-design/icons'
+import { Spin, Empty, notification, Input, Tree, Modal, Dropdown } from 'antd'
+import { FolderOpenOutlined, FolderOutlined, FileOutlined, MoreOutlined } from '@ant-design/icons'
 
 import Api from '@/api'
 import UtilsDM from '@/utils/utils-datamanage.js'
 import MKEmitter from '@/utils/events.js'
 import TimerTask from '@/utils/timer-task.js'
+import asyncComponent from '@/utils/asyncComponent'
 import './index.scss'
 
 const { TreeNode } = Tree
 const { Search } = Input
+
+const MainAction = asyncComponent(() => import('@/tabviews/zshare/actionList'))
 
 class NormalTree extends Component {
   static propTpyes = {
@@ -117,6 +120,8 @@
 
     MKEmitter.addListener('reloadData', this.reloadData)
     MKEmitter.addListener('resetSelectLine', this.resetParentParam)
+    MKEmitter.addListener('queryModuleParam', this.queryModuleParam)
+    MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult)
     
     if (config.timer) {
       this.timer = new TimerTask()
@@ -145,6 +150,8 @@
     }
     MKEmitter.removeListener('reloadData', this.reloadData)
     MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
+    MKEmitter.removeListener('queryModuleParam', this.queryModuleParam)
+    MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
 
     this.timer && this.timer.stop()
   }
@@ -165,6 +172,55 @@
       this.setState({ BID: id }, () => {
         this.loadData()
       })
+    }
+  }
+
+  /**
+   * @description 瀵煎嚭Excel鏃讹紝鑾峰彇椤甸潰鎼滅储鎺掑簭绛夊弬鏁�
+   */
+  queryModuleParam = (menuId, callback) => {
+    const { mainSearch } = this.props
+    const { arr_field, config } = this.state
+
+    if (config.uuid !== menuId) return
+
+    let searches = []
+    if (config.setting.useMSearch && mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      let keys = searches.map(item => item.key.toLowerCase())
+      mainSearch.forEach(item => {
+        if (!keys.includes(item.key.toLowerCase())) {
+          searches.push(item)
+        }
+      })
+    }
+
+    callback({
+      arr_field: arr_field,
+      orderBy: config.setting.order || '',
+      search: searches,
+      menuName: config.name
+    })
+  }
+
+  /**
+   * @description 鎸夐挳鎵ц瀹屾垚鍚庨〉闈㈠埛鏂�
+   * @param {*} menuId     // 鑿滃崟Id
+   * @param {*} position   // 鍒锋柊浣嶇疆
+   * @param {*} btn        // 鎵ц鐨勬寜閽�
+   */
+  refreshByButtonResult = (menuId, position, btn) => {
+    const { config, BID } = this.state
+
+    if (config.uuid !== menuId) return
+
+    if ((position === 'mainline' || position === 'popclose') && config.setting.supModule && BID) { // 鍒锋柊婧愮粍浠舵椂锛岄檮甯﹀埛鏂颁笂绾ц涓庡綋鍓嶇粍浠�
+      MKEmitter.emit('reloadData', config.setting.supModule, BID)
+    } else {
+      this.loadData()
+    }
+
+    if (position === 'popclose') { // 鎵ц鍚姩寮圭獥鐨勬寜閽墍閫夋嫨鐨勫埛鏂伴」
+      btn.$tabId && MKEmitter.emit('refreshPopButton', btn.$tabId)
     }
   }
 
@@ -257,6 +313,7 @@
     data.forEach(item => {
       let pval = item[config.wrap.parentField]
       let val = item[config.wrap.valueField]
+      let uuid = item[config.setting.primaryKey] || ''
 
       if (!val || logMap.has(val)) return
 
@@ -264,6 +321,7 @@
         hasSelectKey = true
         selectData = {
           ...item,
+          $$uuid: uuid,
           $title: item[config.wrap.labelField] || '',
           $key: val,
           $parentId: ''
@@ -274,6 +332,7 @@
       if (pval === config.wrap.mark) {
         parentNodes.push({
           ...item,
+          $$uuid: uuid,
           $title: item[config.wrap.labelField] || '',
           $key: val,
           $parentId: ''
@@ -281,6 +340,7 @@
       } else if (pval) {
         _options.push({
           ...item,
+          $$uuid: uuid,
           $title: item[config.wrap.labelField] || '',
           $key: val,
           $parentId: pval
@@ -309,7 +369,7 @@
       })
 
       setTimeout(() => {
-        MKEmitter.emit('resetSelectLine', config.uuid, _treeNodes[0].$key, _treeNodes[0])
+        MKEmitter.emit('resetSelectLine', config.uuid, _treeNodes[0].$$uuid, _treeNodes[0])
       }, 200)
     } else if (!hasSelectKey && selectKey) {
       this.setState({
@@ -318,7 +378,7 @@
 
       MKEmitter.emit('resetSelectLine', config.uuid, '', '')
     } else if (hasSelectKey) {
-      MKEmitter.emit('resetSelectLine', config.uuid, selectKey, selectData)
+      MKEmitter.emit('resetSelectLine', config.uuid, selectData.$$uuid, selectData)
     }
   }
 
@@ -401,6 +461,37 @@
     })
   }
 
+  renderActionTreeNodes = (nodes) => {
+    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>
+      </>
+
+      if (item.children) {
+        return (
+          <TreeNode icon={<span><FolderOpenOutlined /><FolderOutlined /></span>} title={title} key={item.$key} dataRef={item}>
+            {this.renderActionTreeNodes(item.children)}
+          </TreeNode>
+        )
+      }
+      return <TreeNode icon={<FileOutlined />} key={item.$key} title={title} dataRef={item} isLeaf />
+    })
+  }
+
   changeExpandedKeys = (expandedKeys) => {
     this.setState({
       expandedKeys: expandedKeys
@@ -446,7 +537,7 @@
 
     if (selected) {
       delete _data.children
-      MKEmitter.emit('resetSelectLine', config.uuid, (_data ? _data.$key : ''), _data)
+      MKEmitter.emit('resetSelectLine', config.uuid, (_data ? _data.$$uuid : ''), _data)
     }
 
     this.setState({
@@ -457,6 +548,8 @@
 
   render() {
     const { config, loading, treeNodes, expandedKeys, selectedKeys } = this.state
+
+    let extra = config.action && config.action.length > 0
 
     return (
       <div className="custom-tree-box" id={'anchor' + config.uuid} style={config.style}>
@@ -476,12 +569,12 @@
             onSelect={this.selectTreeNode}
             expandedKeys={expandedKeys}
             selectedKeys={selectedKeys}
-            onRightClick={this.changeExpandedAllKeys}
+            onRightClick={!extra ? this.changeExpandedAllKeys : null}
             onExpand={this.changeExpandedKeys}
             showIcon={config.wrap.showIcon === 'true'}
             showLine={config.wrap.showLine === 'true'}
           >
-            {this.renderTreeNodes(treeNodes)}
+            {!extra ? this.renderTreeNodes(treeNodes) : this.renderActionTreeNodes(treeNodes)}
           </Tree>
         </div> : null}
         {treeNodes && treeNodes.length === 0 ? <Empty description={false}/> : null}
diff --git a/src/tabviews/custom/components/tree/antd-tree/index.scss b/src/tabviews/custom/components/tree/antd-tree/index.scss
index 0ab6c56..b384cef 100644
--- a/src/tabviews/custom/components/tree/antd-tree/index.scss
+++ b/src/tabviews/custom/components/tree/antd-tree/index.scss
@@ -54,6 +54,15 @@
     .ant-tree-node-content-wrapper-open > span > span > .anticon-folder {
       display: none;
     }
+    .ant-tree-node-content-wrapper {
+      position: relative;
+      .anticon-more {
+        position: absolute;
+        right: 0px;
+        padding: 5px 10px;
+        color: var(--mk-sys-color);
+      }
+    }
   }
 
   .tree-box::-webkit-scrollbar {
@@ -80,7 +89,8 @@
     }
   }
   .tree-header + .ant-empty {
-    margin-top: 35px;
+    position: static;
+    margin-top: 20px;
   }
   .loading-mask {
     position: absolute;
@@ -103,3 +113,25 @@
     }
   }
 }
+
+.mk-tree-dropdown-wrap {
+  box-shadow: 0 0 2px #bcbcbc;
+  background: #ffffff;
+  min-width: 85px;
+  .button-list.toolbar-button {
+    padding: 0px;
+  }
+  button {
+    display: block;
+    margin: 0!important;
+    width: 100%;
+    border-radius: 0px;
+    padding-left: 15px!important;
+    .anticon {
+      display: none;
+    }
+    .anticon + span {
+      margin-left: 0px;
+    }
+  }
+}
diff --git a/src/tabviews/custom/index.jsx b/src/tabviews/custom/index.jsx
index 7c1a501..093e914 100644
--- a/src/tabviews/custom/index.jsx
+++ b/src/tabviews/custom/index.jsx
@@ -21,6 +21,7 @@
 const AntvScatter = asyncComponent(() => import('./components/chart/antv-scatter'))
 const DataCard = asyncComponent(() => import('./components/card/data-card'))
 const PropCard = asyncComponent(() => import('./components/card/prop-card'))
+const DoubleDataCard = asyncComponent(() => import('./components/card/double-data-card'))
 const SimpleForm = asyncComponent(() => import('./components/form/simple-form'))
 const StepForm = asyncComponent(() => import('./components/form/step-form'))
 const TabForm = asyncComponent(() => import('./components/form/tab-form'))
@@ -1161,6 +1162,12 @@
             <PropCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
+      } else if (item.type === 'card' && item.subtype === 'dualdatacard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <DoubleDataCard config={item} mainSearch={mainSearch}/>
+          </Col>
+        )
       } else if (item.type === 'table' && item.subtype === 'normaltable') {
         return (
           <Col span={item.width} style={style} key={item.uuid}>
diff --git a/src/tabviews/custom/popview/index.jsx b/src/tabviews/custom/popview/index.jsx
index 6862c55..a3eb065 100644
--- a/src/tabviews/custom/popview/index.jsx
+++ b/src/tabviews/custom/popview/index.jsx
@@ -20,6 +20,7 @@
 const AntvScatter = asyncComponent(() => import('../components/chart/antv-scatter'))
 const DataCard = asyncComponent(() => import('../components/card/data-card'))
 const PropCard = asyncComponent(() => import('../components/card/prop-card'))
+const DoubleDataCard = asyncComponent(() => import('../components/card/double-data-card'))
 const SimpleForm = asyncComponent(() => import('../components/form/simple-form'))
 const StepForm = asyncComponent(() => import('../components/form/step-form'))
 const TabForm = asyncComponent(() => import('../components/form/tab-form'))
@@ -864,6 +865,12 @@
             <PropCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
+      } else if (item.type === 'card' && item.subtype === 'dualdatacard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <DoubleDataCard config={item} mainSearch={mainSearch}/>
+          </Col>
+        )
       } else if (item.type === 'table' && item.subtype === 'basetable') {
         return (
           <Col span={item.width} style={style} key={item.uuid}>
diff --git a/src/tabviews/subtable/index.jsx b/src/tabviews/subtable/index.jsx
index b261601..150533f 100644
--- a/src/tabviews/subtable/index.jsx
+++ b/src/tabviews/subtable/index.jsx
@@ -241,6 +241,7 @@
       config.action.forEach(item => {
         item.logLabel = Tab.label + '-' + item.label // 鐢ㄤ簬sPC_TableData_InUpDe璁板綍鎿嶄綔鎸夐挳
         item.$menuId = this.props.MenuID
+        item.$old = true
 
         if (item.OpenType === 'funcbutton' && item.funcType === 'print' && item.verify) { // 鎵撳嵃鏈鸿缃�
           let _item = window.GLOB.UserCacheMap.get(Tab.uuid + item.uuid)
diff --git a/src/tabviews/subtabtable/index.jsx b/src/tabviews/subtabtable/index.jsx
index 154233c..c46137c 100644
--- a/src/tabviews/subtabtable/index.jsx
+++ b/src/tabviews/subtabtable/index.jsx
@@ -211,6 +211,7 @@
       config.action.forEach(item => {
         item.logLabel = Tab.label + '-' + item.label // 鐢ㄤ簬sPC_TableData_InUpDe璁板綍鎿嶄綔鎸夐挳
         item.$menuId = this.props.MenuID
+        item.$old = true
 
         if (item.position === 'toolbar') {
           item.$toolbtn = true
diff --git a/src/tabviews/zshare/actionList/normalbutton/index.jsx b/src/tabviews/zshare/actionList/normalbutton/index.jsx
index 3a5c539..2271803 100644
--- a/src/tabviews/zshare/actionList/normalbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/normalbutton/index.jsx
@@ -2424,7 +2424,20 @@
         this.setState({
           visible: true
         })
+
+        if (btnconfig.setting.display === 'modal' && btnconfig.setting.moveable === 'true') {
+          setTimeout(() => {
+            this.setMove()
+          }, 100)
+        }
       }
+    } else if (!btn.$old) {
+      notification.warning({
+        top: 92,
+        message: '鏈幏鍙栧埌鎸夐挳閰嶇疆淇℃伅锛�',
+        duration: 5
+      })
+      this.setState({ loading: false })
     } else {
       Api.getCacheConfig({
         func: 'sPC_Get_LongParam',
@@ -2677,15 +2690,17 @@
         width = btnconfig.setting.width > 100 ? btnconfig.setting.width : btnconfig.setting.width + '%'
         container = () => document.getElementById(btn.ContainerId)
       }
+
       return (
         <Modal
           title={title}
           maskClosable={clickouter}
           getContainer={container}
-          wrapClassName='action-modal'
+          wrapClassName={'action-modal' + (btnconfig.setting.moveable === 'true' ? ' moveable-modal modal-' + btn.uuid : '')}
           visible={visible}
           width={width}
           onOk={this.handleOk}
+          maskStyle={btnconfig.setting.moveable === 'true' ?  {backgroundColor: 'rgba(0, 0, 0, 0.15)'} : null}
           confirmLoading={this.state.confirmLoading}
           onCancel={this.handleCancel}
           destroyOnClose
@@ -2704,6 +2719,43 @@
     }
   }
 
+  setMove = () => {
+    const { btn } = this.props
+    let wrap = document.getElementsByClassName('modal-' + btn.uuid)[0]
+
+    if (!wrap) return
+
+    let node = wrap.getElementsByClassName('ant-modal-header')[0]
+
+    if (!node) return
+
+    node.onmousedown = function(e) {
+      let orileft = 0
+      let oritop = 0
+      let oleft = e.clientX
+      let otop = e.clientY
+
+      if (node.parentNode.style.left) {
+        orileft = parseFloat(node.parentNode.style.left)
+      }
+      if (node.parentNode.style.top) {
+        oritop = parseFloat(node.parentNode.style.top)
+      }
+
+      document.onmousemove = function(e) {
+        let left = e.clientX - oleft
+        let top = e.clientY - otop
+
+        node.parentNode.style.left = (orileft + left) + 'px'
+        node.parentNode.style.top = (oritop + top) + 'px'
+      }
+    }
+
+    document.onmouseup = function() {
+      document.onmousemove = null
+    }
+  }
+
   render() {
     const { btn } = this.props
     const { loadingNumber, loadingTotal, loading, disabled, hidden, check } = this.state
diff --git a/src/tabviews/zshare/actionList/printbutton/index.jsx b/src/tabviews/zshare/actionList/printbutton/index.jsx
index 9fa8f51..d868d58 100644
--- a/src/tabviews/zshare/actionList/printbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/printbutton/index.jsx
@@ -2013,7 +2013,20 @@
         this.setState({
           visible: true
         })
+
+        if (btnconfig.setting.display === 'modal' && btnconfig.setting.moveable === 'true') {
+          setTimeout(() => {
+            this.setMove()
+          }, 100)
+        }
       }
+    } else if (!btn.$old) {
+      notification.warning({
+        top: 92,
+        message: '鏈幏鍙栧埌鎸夐挳閰嶇疆淇℃伅锛�',
+        duration: 5
+      })
+      this.setState({ loading: false })
     } else {
       Api.getCacheConfig({
         func: 'sPC_Get_LongParam',
@@ -2208,10 +2221,11 @@
         title={title}
         maskClosable={clickouter}
         getContainer={container}
-        wrapClassName='action-modal'
+        wrapClassName={'action-modal' + (btnconfig.setting.moveable === 'true' ? ' moveable-modal modal-' + btn.uuid : '')}
         visible={this.state.visible}
         confirmLoading={this.state.confirmLoading}
         width={width}
+        maskStyle={btnconfig.setting.moveable === 'true' ?  {backgroundColor: 'rgba(0, 0, 0, 0.15)'} : null}
         onOk={this.handleOk}
         onCancel={this.handleCancel}
         destroyOnClose
@@ -2229,6 +2243,43 @@
     )
   }
 
+  setMove = () => {
+    const { btn } = this.props
+    let wrap = document.getElementsByClassName('modal-' + btn.uuid)[0]
+
+    if (!wrap) return
+
+    let node = wrap.getElementsByClassName('ant-modal-header')[0]
+
+    if (!node) return
+
+    node.onmousedown = function(e) {
+      let orileft = 0
+      let oritop = 0
+      let oleft = e.clientX
+      let otop = e.clientY
+
+      if (node.parentNode.style.left) {
+        orileft = parseFloat(node.parentNode.style.left)
+      }
+      if (node.parentNode.style.top) {
+        oritop = parseFloat(node.parentNode.style.top)
+      }
+
+      document.onmousemove = function(e) {
+        let left = e.clientX - oleft
+        let top = e.clientY - otop
+
+        node.parentNode.style.left = (orileft + left) + 'px'
+        node.parentNode.style.top = (oritop + top) + 'px'
+      }
+    }
+
+    document.onmouseup = function() {
+      document.onmousemove = null
+    }
+  }
+
   render() {
     const { btn } = this.props
     const { loading, disabled, hidden } = this.state
diff --git a/src/tabviews/zshare/mutilform/index.jsx b/src/tabviews/zshare/mutilform/index.jsx
index e69e00d..325f5ab 100644
--- a/src/tabviews/zshare/mutilform/index.jsx
+++ b/src/tabviews/zshare/mutilform/index.jsx
@@ -41,6 +41,7 @@
 
   state = {
     formlist: [],    // 琛ㄥ崟椤�
+    ID: ''
   }
 
   record = {}
@@ -506,7 +507,7 @@
 
     this.record = record
 
-    this.setState({ formlist }, () => {
+    this.setState({ formlist, ID: this.props.data ? this.props.data.$$uuid || '' : '' }, () => {
       if (unload) return
       
       if (action.setting && action.setting.focus && fieldMap.has(action.setting.focus)) {
@@ -565,7 +566,8 @@
       obj_name: '',
       arr_field: '',
       table_type: 'Y',
-      BID: BID || ''
+      BID: BID || '',
+      ID: this.state.ID || ''
     }
 
     if (param.LText) {
@@ -600,7 +602,8 @@
       obj_name: '',
       arr_field: '',
       table_type: 'Y',
-      BID: BID || ''
+      BID: BID || '',
+      ID: this.state.ID || ''
     }
 
     if (mainparam.LText) {
@@ -661,7 +664,8 @@
         LText: _sql + form.base_sql,
         obj_name: form.field,
         arr_field: form.arr_field,
-        BID: this.props.BID || ''
+        BID: this.props.BID || '',
+        ID: this.state.ID || ''
       }
 
       if (debug) {
@@ -886,6 +890,7 @@
 
     formlist.forEach((item, index) => {
       if (item.hidden) return
+      if (item.empty === 'hidden' && item.options.length === 0) return
 
       if (item.type === 'split') {
         fields.push(
diff --git a/src/tabviews/zshare/topSearch/index.jsx b/src/tabviews/zshare/topSearch/index.jsx
index 85fbb90..8f1b7d4 100644
--- a/src/tabviews/zshare/topSearch/index.jsx
+++ b/src/tabviews/zshare/topSearch/index.jsx
@@ -77,6 +77,7 @@
         _setting.labelwidth = config.wrap.searchLwidth !== undefined ? config.wrap.searchLwidth : 33.3
         _setting.labelCol = {style: {width: _setting.labelwidth + '%'}}
         _setting.wrapperCol = {style: {width: (100 - _setting.labelwidth) + '%'}}
+        _setting.borderRadius = config.wrap.borderRadius
       }
       _setting.style = null
       
@@ -552,6 +553,11 @@
     })
 
     if (setting.show || setting.showAdv) {
+      let style = {}
+      if (setting.borderRadius) {
+        style.borderRadius = setting.borderRadius
+      }
+
       fields.push(
         <Col span={setting.ratio} style={{ whiteSpace: 'nowrap' }} className="mk-search-col search-button" key="actions">
           <Form.Item
@@ -560,10 +566,10 @@
             labelCol={setting.labelCol}
             wrapperCol={setting.wrapperCol}
           >
-            {setting.show ? <Button type="primary" onClick={this.handleSubmit}>
+            {setting.show ? <Button style={style} type="primary" onClick={this.handleSubmit}>
               鎼滅储
             </Button> : null}
-            {setting.show ? <Button style={{ marginLeft: 8 }} onClick={this.handleReset}>
+            {setting.show ? <Button style={{ marginLeft: 8, ...style }} onClick={this.handleReset}>
               閲嶇疆
             </Button> : null}
             {setting.showAdv ? <Button className={visible ? 'visible' : ''} type="link" onClick={this.handleAdvance}>
diff --git a/src/templates/comtableconfig/updatetable/index.jsx b/src/templates/comtableconfig/updatetable/index.jsx
index 8aaf83b..ffe2380 100644
--- a/src/templates/comtableconfig/updatetable/index.jsx
+++ b/src/templates/comtableconfig/updatetable/index.jsx
@@ -705,21 +705,13 @@
       _resolve()
 
       if (res.status) {
-        notification.success({
-          top: 92,
-          message: '鍗囩骇鎴愬姛',
-          duration: 2
-        })
-
         delete urlparam.type
         delete urlparam.MenuType
 
         let _param = window.btoa(window.encodeURIComponent(JSON.stringify(urlparam)))
 
-        setTimeout(() => {
-          window.history.replaceState(null, null, window.location.href.split('#')[0] + `#/tabledesign/${_param}`)
-          window.location.reload()
-        }, 2000)
+        window.history.replaceState(null, null, window.location.href.split('#')[0] + `#/tabledesign/${_param}`)
+        window.location.reload()
       } else {
         Modal.warning({
           title: res.message,
@@ -821,6 +813,7 @@
           if (m.signType === 'line') {
             lineMarks.push({
               $index: lineMarks.length + 1,
+              uuid: Utils.getuuid(),
               field: field,
               color: color,
               contrastValue: m.contrastValue || '',
@@ -830,6 +823,7 @@
           } else {
             _col.marks.push({
               $index: _col.marks.length + 1,
+              uuid: Utils.getuuid(),
               field: field,
               color: color,
               contrastValue: m.contrastValue || '',
diff --git a/src/templates/formtabconfig/index.jsx b/src/templates/formtabconfig/index.jsx
index 028b214..851fb46 100644
--- a/src/templates/formtabconfig/index.jsx
+++ b/src/templates/formtabconfig/index.jsx
@@ -364,7 +364,6 @@
 
     _inputfields = _formfields.filter(item => ['text', 'number', 'textarea', 'color'].includes(item.type) && card.field !== item.field)
     _tabfields = _formfields.filter(item => card.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type))
-    _tabfields.unshift({field: '', text: '鍘熻〃鍗�'})
     
     if (card.linkSubField && card.linkSubField.length > 0) {
       let fields = _inputfields.map(item => item.field)
@@ -380,12 +379,12 @@
         uniq.set(item.field, true)
 
         _linkableFields.push({
-          value: item.field,
-          text: item.label + ' (琛ㄥ崟)'
+          field: item.field,
+          label: item.label + '-琛ㄥ崟'
         })
         _linksupFields.push({
-          value: item.field,
-          text: item.label
+          field: item.field,
+          label: item.label
         })
       }
     })
@@ -396,8 +395,8 @@
           uniq.set(col.field, true)
 
           _linkableFields.push({
-            value: col.field,
-            text: col.label + ' (鏄剧ず鍒�)'
+            field: col.field,
+            label: col.label + '-鏄剧ず鍒�'
           })
         }
       })
diff --git a/src/templates/modalconfig/index.jsx b/src/templates/modalconfig/index.jsx
index 7ec9759..2a2d203 100644
--- a/src/templates/modalconfig/index.jsx
+++ b/src/templates/modalconfig/index.jsx
@@ -249,35 +249,28 @@
     let index = null
     uniq.set(card.field, true)
 
-    let _inputIndex = 1
-    let _tabIndex = 1
-    let _linkIndex = 1
     config.fields.forEach((item, i) => {
       if (card.uuid === item.uuid) {
         index = i
       }
 
-      let label = `${item.field || ''}锛�${item.label}锛塦
-
       if (['text', 'number', 'textarea', 'color'].includes(item.type) && card.field !== item.field) {
         _inputfields.push({
           field: item.field,
-          label: _inputIndex + '銆�' + label
+          label: item.label
         })
-        _inputIndex++
       }
       if (card.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type)) {
         _tabfields.push({
           field: item.field,
-          label: _tabIndex + '銆�' + label
+          label: item.label
         })
-        _tabIndex++
       }
 
       if (item.type === 'switch') {
         _linksupFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + label
+          label: item.label
         })
       }
 
@@ -288,17 +281,14 @@
 
         _linkableFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + item.label + ' (琛ㄥ崟)'
+          label: item.label + '-琛ㄥ崟'
         })
         _linksupFields.push({
           field: item.field,
-          label: _linkIndex + '銆�' + label
+          label: item.label
         })
-        _linkIndex++
       }
     })
-
-    _tabfields.unshift({field: '', label: '鍘熻〃鍗�'})
 
     if (index !== null) {
       if (index === 0) {
@@ -315,9 +305,8 @@
 
           _linkableFields.push({
             field: col.field,
-            label: _linkIndex + '銆�' + col.label + ' (鏄剧ず鍒�)'
+            label: col.label + '-鏄剧ず鍒�'
           })
-          _linkIndex++
         }
       })
     } else if (tabConfig) {
@@ -327,9 +316,8 @@
 
           _linkableFields.push({
             field: col.field,
-            label: _linkIndex + '銆�' + col.label + ' (鏄剧ず鍒�)'
+            label: col.label + '-鏄剧ず鍒�'
           })
-          _linkIndex++
         }
       })
     } else if (menu.LongParam) {
@@ -339,9 +327,8 @@
 
           _linkableFields.push({
             field: col.field,
-            label: _linkIndex + '銆�' + col.label + ' (鏄剧ず鍒�)'
+            label: col.label + '-鏄剧ず鍒�'
           })
-          _linkIndex++
         }
       })
     }
diff --git a/src/templates/modalconfig/settingform/index.jsx b/src/templates/modalconfig/settingform/index.jsx
index e3b7b22..ab2adf7 100644
--- a/src/templates/modalconfig/settingform/index.jsx
+++ b/src/templates/modalconfig/settingform/index.jsx
@@ -111,7 +111,12 @@
             </Form.Item>
           </Col> */}
           <Col span={24}>
-            <Form.Item label="鏄剧ず鏂瑰紡">
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="鍙�夋嫨琛ㄥ崟鐨勬樉绀哄舰寮忥紝娉細鏍囩鎵撳嵃鎸夐挳锛屾殏涓嶆敮鎸佷互鎶藉眽鏄剧ず琛ㄥ崟銆�">
+                <QuestionCircleOutlined className="mk-form-tip" />
+                鏄剧ず鏂瑰紡
+              </Tooltip>
+            }>
               {getFieldDecorator('display', {
                 initialValue: display || 'modal'
               })(
@@ -335,6 +340,23 @@
               )}
             </Form.Item>
           </Col> : null}
+          {!appType && display === 'modal' ? <Col span={12}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="鏄惁鍙嫋鎷界Щ鍔ㄦā鎬佹銆�">
+                <QuestionCircleOutlined className="mk-form-tip" />
+                鍙Щ鍔�
+              </Tooltip>
+            }>
+              {getFieldDecorator('moveable', {
+                initialValue: config.setting.moveable || 'false'
+              })(
+                <Radio.Group>
+                  <Radio value="false">鍚�</Radio>
+                  <Radio value="true">鏄�</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col> : null}
         </Row>
       </Form>
     )
diff --git a/src/templates/sharecomponent/searchcomponent/dragsearch/index.jsx b/src/templates/sharecomponent/searchcomponent/dragsearch/index.jsx
index 9f92dd0..fdd7f52 100644
--- a/src/templates/sharecomponent/searchcomponent/dragsearch/index.jsx
+++ b/src/templates/sharecomponent/searchcomponent/dragsearch/index.jsx
@@ -139,6 +139,10 @@
   let radio = setting.searchRatio || 6
   let labelwidth = setting.searchLwidth !== undefined ? setting.searchLwidth : 33.3
   let advanceType = setting.advanceType || 'modal'
+  let style = {}
+  if (setting.borderRadius) {
+    style.borderRadius = setting.borderRadius
+  }
 
   return (
     <div ref={drop} className="ant-row">
@@ -170,8 +174,8 @@
             </div>
           } trigger="hover">
             <div className="ant-col ant-form-item-control-wrapper">
-              <Button type="primary">鎼滅储</Button>
-              <Button style={{ marginLeft: 8 }}>閲嶇疆</Button>
+              <Button style={style} type="primary">鎼滅储</Button>
+              <Button style={{ marginLeft: 8, ...style }}>閲嶇疆</Button>
               <div style={{position: 'absolute', top: 0, bottom: 0, left: 0, right: 0}}></div>
             </div>
           </Popover>
diff --git a/src/templates/sharecomponent/searchcomponent/settingform/index.jsx b/src/templates/sharecomponent/searchcomponent/settingform/index.jsx
index a131ad6..1adb220 100644
--- a/src/templates/sharecomponent/searchcomponent/settingform/index.jsx
+++ b/src/templates/sharecomponent/searchcomponent/settingform/index.jsx
@@ -140,6 +140,18 @@
               })(<InputNumber min={0} max={100} precision={1} onPressEnter={this.props.inputSubmit}/>)}
             </Form.Item>
           </Col>
+          <Col span={12}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="鎼滅储鍜岄噸缃寜閽殑鍦嗚銆�">
+                <QuestionCircleOutlined className="mk-form-tip" />
+                鎸夐挳鍦嗚
+              </Tooltip>
+            }>
+              {getFieldDecorator('borderRadius', {
+                initialValue: setting.borderRadius || '',
+              })(<InputNumber min={0} precision={0} onPressEnter={this.props.inputSubmit}/>)}
+            </Form.Item>
+          </Col>
         </Row>
       </Form>
     )
diff --git a/src/templates/zshare/formconfig.jsx b/src/templates/zshare/formconfig.jsx
index 83db912..d11afb9 100644
--- a/src/templates/zshare/formconfig.jsx
+++ b/src/templates/zshare/formconfig.jsx
@@ -2383,7 +2383,7 @@
 /**
  * @description 鑾峰彇琛ㄥ崟閰嶇疆淇℃伅
  * @param {*} card            // 琛ㄥ崟瀵硅薄
- * @param {*} inputfields     // 鍙叧鑱旇〃鍗�
+ * @param {*} inputfields     // 鍙啓鍏ヨ〃鍗�
  * @param {*} tabfields       // 鍙垏鎹㈣〃鍗�
  * @param {*} linkableFields  // 鍙叧鑱旇〃鍗�
  * @param {*} linksupFields   // 涓婄骇琛ㄥ崟
@@ -2407,6 +2407,27 @@
   } else {
     roleList = []
   }
+
+  inputfields = inputfields.map((item, index) => {
+    item.label = `${index + 1}銆�${item.field || ''}锛�${item.label}锛塦
+    return item
+  })
+  tabfields = tabfields.map((item, index) => {
+    item.label = `${index + 1}銆�${item.field || ''}锛�${item.label}锛塦
+    return item
+  })
+
+  tabfields.unshift({field: '', label: '鍘熻〃鍗�'})
+
+  linkableFields = linkableFields.map((item, index) => {
+    item.label = `${index + 1}銆�${item.field || ''}锛�${item.label}锛塦
+    return item
+  })
+
+  linksupFields = linksupFields.map((item, index) => {
+    item.label = `${index + 1}銆�${item.field || ''}锛�${item.label}锛塦
+    return item
+  })
   
   let _openType = [{
     value: 'text',
@@ -3817,6 +3838,18 @@
     //   forbid: appType !== 'mob'
     // },
     {
+      type: 'radio',
+      key: 'empty',
+      label: '绌哄�奸殣钘�',
+      initVal: card.empty || 'show',
+      tooltip: '褰撻�夐」涓虹┖鏃讹紝闅愯棌璇ヨ〃鍗曘��',
+      required: false,
+      options: [
+        {value: 'show', text: '鍚�'},
+        {value: 'hidden', text: '鏄�'},
+      ]
+    },
+    {
       type: 'multiselect',
       key: 'blacklist',
       label: '榛戝悕鍗�',
diff --git a/src/templates/zshare/modalform/index.jsx b/src/templates/zshare/modalform/index.jsx
index 4a6f1e1..6bd3be2 100644
--- a/src/templates/zshare/modalform/index.jsx
+++ b/src/templates/zshare/modalform/index.jsx
@@ -21,11 +21,11 @@
 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'],
   number: ['initval', 'readonly', 'hidden', 'decimal', 'min', 'max', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'enter', 'cursor', 'splitline', 'place', 'marginTop', 'marginBottom'],
-  select: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'declare', 'setAll', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'emptyText', 'enter', 'splitline', 'dropdown', 'marginTop', 'marginBottom'],
-  checkbox: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'fieldlength', 'span', 'labelwidth', 'tooltip', 'extra', 'splitline', 'arrange', 'marginTop', 'marginBottom'],
-  radio: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'declare', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'setAll', 'emptyText', 'splitline', 'arrange', 'marginTop', 'marginBottom'],
-  checkcard: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'span', 'labelwidth', 'display', 'tooltip', 'extra', 'width', 'multiple', 'splitline', 'marginTop', 'marginBottom'],
-  multiselect: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'fieldlength', 'span', 'labelwidth', 'tooltip', 'extra', 'marginTop', 'marginBottom', 'dropdown'],
+  select: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'declare', 'setAll', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'emptyText', 'enter', 'splitline', 'dropdown', 'marginTop', 'marginBottom', 'empty'],
+  checkbox: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'fieldlength', 'span', 'labelwidth', 'tooltip', 'extra', 'splitline', 'arrange', 'marginTop', 'marginBottom', 'empty'],
+  radio: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'declare', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'setAll', 'emptyText', 'splitline', 'arrange', 'marginTop', 'marginBottom', 'empty'],
+  checkcard: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'span', 'labelwidth', 'display', 'tooltip', 'extra', 'width', 'multiple', 'splitline', 'marginTop', 'marginBottom', 'empty'],
+  multiselect: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'fieldlength', 'span', 'labelwidth', 'tooltip', 'extra', 'marginTop', 'marginBottom', 'dropdown', 'empty'],
   link: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'declare', 'setAll', 'linkField', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'emptyText', 'enter', 'splitline', 'dropdown', 'marginTop', 'marginBottom'],
   fileupload: ['readonly', 'required', 'readin', 'fieldlength', 'maxfile', 'fileType', 'span', 'labelwidth', 'tooltip', 'extra', 'compress', 'miniSet', 'splitline', 'marginTop', 'marginBottom', 'maxSize'],
   switch: ['initval', 'openVal', 'closeVal', 'openText', 'closeText', 'readonly', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'splitline', 'marginTop', 'marginBottom'],
@@ -33,7 +33,7 @@
   datemonth: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'declareType', 'splitline', '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', 'count', 'placeholder', 'marginTop', 'marginBottom'],
-  cascader: ['readonly', 'required', 'hidden', 'readin', 'resourceType', 'fieldlength', 'span', 'labelwidth', 'tooltip', 'extra', 'splitline', 'marginTop', 'marginBottom', 'separator'],
+  cascader: ['readonly', 'required', 'hidden', 'readin', 'resourceType', 'fieldlength', 'span', 'labelwidth', 'tooltip', 'extra', 'splitline', 'marginTop', 'marginBottom', 'separator', 'empty'],
   color: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'colorType', 'extra', 'marginTop', 'marginBottom'],
   rate: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'splitline', 'tooltip', 'extra', 'marginTop', 'marginBottom', 'allowHalf', 'color', 'rateCount', 'character', 'place'],
   hint: ['label', 'field', 'type', 'blacklist', 'message', 'span', 'labelwidth', 'splitline', 'marginTop', 'marginBottom'],
@@ -66,7 +66,7 @@
 
       if (item.key === 'linkSubField') {
         item.options.forEach(cell => {
-          transfield[cell.field] = cell.label
+          transfield[cell.field] = cell.label.replace(/^\d+銆�/, '')
         })
       }
     })
diff --git a/src/templates/zshare/modalform/modaleditable/index.jsx b/src/templates/zshare/modalform/modaleditable/index.jsx
index 9edfdf9..5e84435 100644
--- a/src/templates/zshare/modalform/modaleditable/index.jsx
+++ b/src/templates/zshare/modalform/modaleditable/index.jsx
@@ -168,7 +168,7 @@
 
         if (col.dataIndex !== 'operation') {
           col.title = <div>
-            Value
+            {col.$title}
             <Popconfirm
               title={`纭畾鍒囨崲涓�${col.datatype === 'number' ? '鏂囨湰' : '鏁板��'}鍚楋紵`}
               overlayClassName="popover-confirm"
@@ -309,6 +309,7 @@
       fields = subFields.map(field => {
         return {
           title: transfield[field] || field,
+          $title: transfield[field] || field,
           dataIndex: field,
           editable: true,
           datatype: dataItem && typeof(dataItem[field]) === 'number' ? 'number' : 'string'
@@ -321,12 +322,14 @@
     let columns = [
       {
         title: 'Value',
+        $title: 'Value',
         dataIndex: 'Value',
         editable: true,
         datatype: dataItem && typeof(dataItem.Value) === 'number' ? 'number' : 'string'
       },
       {
         title: 'Text',
+        $title: 'Text',
         dataIndex: 'Text',
         editable: true,
         datatype: dataItem && typeof(dataItem.Text) === 'number' ? 'number' : 'string'
@@ -358,6 +361,7 @@
     if (type === 'link') {
       columns.unshift({
         title: 'ParentID',
+        $title: 'ParentID',
         dataIndex: 'ParentID',
         editable: true,
         datatype: dataItem && typeof(dataItem.ParentID) === 'number' ? 'number' : 'string'
@@ -368,7 +372,7 @@
       columns: columns.map(col => {
         if (col.dataIndex !== 'operation') {
           col.title = <div>
-            Value
+            {col.$title}
             <Popconfirm
               title={`纭畾鍒囨崲涓�${col.datatype === 'number' ? '鏂囨湰' : '鏁板��'}鍚楋紵`}
               overlayClassName="popover-confirm"
diff --git a/src/templates/zshare/modalform/modaleditable/index.scss b/src/templates/zshare/modalform/modaleditable/index.scss
index c802fbe..7c097fd 100644
--- a/src/templates/zshare/modalform/modaleditable/index.scss
+++ b/src/templates/zshare/modalform/modaleditable/index.scss
@@ -21,9 +21,9 @@
 
     .anticon-swap {
       position: absolute;
-      right: 16px;
+      right: 12px;
       font-size: 14px;
-      top: 12px;
+      // top: 12px;
       color: #b8b8b8;
     }
   }
diff --git a/src/templates/zshare/verifycard/index.jsx b/src/templates/zshare/verifycard/index.jsx
index ab8d74c..b2be305 100644
--- a/src/templates/zshare/verifycard/index.jsx
+++ b/src/templates/zshare/verifycard/index.jsx
@@ -1154,6 +1154,8 @@
       verify.cbScripts.push(values)
     }
 
+    MKEmitter.emit('editLineId', values.uuid)
+
     this.setState({ verify })
   }
 
diff --git a/src/utils/utils-datamanage.js b/src/utils/utils-datamanage.js
index 29651eb..80b560e 100644
--- a/src/utils/utils-datamanage.js
+++ b/src/utils/utils-datamanage.js
@@ -213,9 +213,17 @@
     let LText = ''
     let DateCount = ''
 
+    if (setting.sub_field) {
+      arrFields = arrFields + ',' + setting.sub_field
+    }
+
     if (_dataresource && setting.laypage && orderBy && !id) {
       LText = `/*system_query*/select top ${pageSize} ${arrFields} from (select ${arrFields} ,ROW_NUMBER() over(order by ${orderBy}) as rows from ${_dataresource} ${_search}) tmptable where rows > ${pageSize * (pageIndex - 1)} order by tmptable.rows `
-      DateCount = `/*system_query*/select count(1) as total from ${_dataresource} ${_search}`
+      if (setting.sub_field) {
+        DateCount = `/*system_query*/select count(1) as total from (select distinct ${setting.primaryKey} from ${_dataresource} ${_search})a`
+      } else {
+        DateCount = `/*system_query*/select count(1) as total from ${_dataresource} ${_search}`
+      }
     } else if (_dataresource && orderBy) {
       LText = `/*system_query*/select ${arrFields} from (select ${arrFields} ,ROW_NUMBER() over(order by ${orderBy}) as rows from ${_dataresource} ${_search}) tmptable order by tmptable.rows `
     } else if (_dataresource) {
@@ -276,6 +284,13 @@
     // param.sub_name = 'sub_data'
     // param.sub_field = 'BID,friend_text,icon,Initials'
 
+    if (setting.sub_field) {
+      param.sub_name = setting.subdata
+      param.tabid = setting.primaryKey || ''
+      param.parid = setting.subBID || ''
+      param.sub_field = setting.sub_field
+    }
+
     // exec_type: 'y' 瑙g爜瀛楁锛歀Text銆丩Text1銆丩Text2銆乧ustom_script銆丏ateCount
 
     param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
diff --git a/src/views/billprint/index.jsx b/src/views/billprint/index.jsx
index 8b1b229..f69b43a 100644
--- a/src/views/billprint/index.jsx
+++ b/src/views/billprint/index.jsx
@@ -21,6 +21,7 @@
 const AntvScatter = asyncComponent(() => import('@/tabviews/custom/components/chart/antv-scatter'))
 const DataCard = asyncComponent(() => import('@/tabviews/custom/components/card/data-card'))
 const PropCard = asyncComponent(() => import('@/tabviews/custom/components/card/prop-card'))
+const DoubleDataCard = asyncComponent(() => import('@/tabviews/custom/components/card/double-data-card'))
 const TableCard = asyncComponent(() => import('@/tabviews/custom/components/card/table-card'))
 const NormalTable = asyncComponent(() => import('@/tabviews/custom/components/table/normal-table'))
 const SandBox = asyncComponent(() => import('@/tabviews/custom/components/code/sand-box'))
@@ -837,6 +838,12 @@
             <PropCard config={item} initdata={item.data} mainSearch={[]} />
           </Col>
         )
+      } else if (item.type === 'card' && item.subtype === 'dualdatacard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <DoubleDataCard config={item} mainSearch={[]}/>
+          </Col>
+        )
       } else if (item.type === 'card' && item.subtype === 'tablecard') {
         return (
           <Col span={item.width} style={style} key={item.uuid}>
diff --git a/src/views/mobdesign/index.jsx b/src/views/mobdesign/index.jsx
index 587199b..f54144d 100644
--- a/src/views/mobdesign/index.jsx
+++ b/src/views/mobdesign/index.jsx
@@ -1068,6 +1068,17 @@
                 title: cell.label,
               })
             })
+
+            if (item.subtype !== 'dualdatacard') return
+            
+            card.backElements && card.backElements.forEach(cell => {
+              if (cell.eleType !== 'button' || cell.hidden === 'true') return
+
+              m.children.push({
+                key: cell.uuid,
+                title: cell.label,
+              })
+            })
           })
         } else if (item.type === 'balcony') {
           item.elements && item.elements.forEach(cell => {
diff --git a/src/views/tabledesign/index.jsx b/src/views/tabledesign/index.jsx
index d368f6b..5a0b0c8 100644
--- a/src/views/tabledesign/index.jsx
+++ b/src/views/tabledesign/index.jsx
@@ -386,6 +386,20 @@
             style: { backgroundColor: '#ffffff', paddingTop: '16px', paddingBottom: '80px', paddingLeft: '16px', paddingRight: '16px'},
           }
         } else {
+          if (config.Template === 'CommonTable' && !config.components) {
+            let urlparam = JSON.parse(window.decodeURIComponent(window.atob(this.props.match.params.param)))
+            urlparam.type = 'CommonTable'
+            if (urlparam.PageParam) {
+              urlparam.PageParam.Template = 'CommonTable'
+            }
+            urlparam = window.btoa(window.encodeURIComponent(JSON.stringify(urlparam)))
+
+            let url = `#/basedesign/${urlparam}`
+            
+            window.history.replaceState(null, null, window.location.href.split('#')[0] + url)
+            window.location.reload()
+            return
+          }
           config.uuid = MenuId
           config.MenuID = MenuId
           config.Template = 'BaseTable'

--
Gitblit v1.8.0