From 8137ac074ce6370e4b46295e7acf9c7870ef82d2 Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期五, 17 二月 2023 22:19:23 +0800
Subject: [PATCH] 2023-02-17

---
 src/utils/utils-datamanage.js                                     |    7 
 src/pc/modulesource/option.jsx                                    |    1 
 src/tabviews/custom/components/table/base-table/index.jsx         |    6 
 src/menu/components/card/doublecardcomponent/options.jsx          |   58 +
 src/tabviews/custom/components/card/double-data-card/index.scss   |  308 ++++++++++
 src/tabviews/custom/components/card/double-data-card/index.jsx    |  954 +++++++++++++++++++++++++++++++
 src/tabviews/custom/components/card/cardItem/index.jsx            |    4 
 src/menu/components/card/doublecardcomponent/index.scss           |   47 +
 src/mob/modulesource/option.jsx                                   |    1 
 src/tabviews/custom/components/carousel/data-card/index.jsx       |   13 
 src/tabviews/custom/popview/index.jsx                             |    7 
 src/tabviews/custom/components/table/edit-table/index.jsx         |   15 
 src/views/mobdesign/index.jsx                                     |   11 
 src/mob/mobshell/card.jsx                                         |    3 
 src/menu/modulesource/option.jsx                                  |    2 
 src/tabviews/custom/components/card/data-card/index.jsx           |   22 
 src/tabviews/custom/components/card/table-card/index.jsx          |   15 
 src/tabviews/custom/components/card/prop-card/index.jsx           |   18 
 src/menu/components/card/double-data-card/options.jsx             |   86 +-
 src/tabviews/custom/components/share/tabtransfer/index.jsx        |    7 
 src/tabviews/custom/components/group/normal-group/index.jsx       |    7 
 src/menu/datasource/verifycard/settingform/index.jsx              |   15 
 src/tabviews/custom/components/table/normal-table/index.jsx       |   15 
 src/menu/components/card/double-data-card/index.jsx               |   13 
 src/menu/components/card/doublecardcomponent/index.jsx            |   74 +
 src/mob/components/tabs/tabcomponents/card.jsx                    |    3 
 src/tabviews/custom/components/timeline/normal-timeline/index.jsx |   12 
 src/menu/components/card/cardcomponent/index.jsx                  |   17 
 src/menu/components/group/groupcomponents/card.jsx                |    3 
 src/tabviews/custom/components/carousel/prop-card/index.jsx       |   12 
 src/tabviews/custom/index.jsx                                     |    7 
 src/menu/components/card/cardcomponent/index.scss                 |   31 +
 src/menu/components/tabs/tabcomponents/card.jsx                   |    3 
 33 files changed, 1,589 insertions(+), 208 deletions(-)

diff --git a/src/menu/components/card/cardcomponent/index.jsx b/src/menu/components/card/cardcomponent/index.jsx
index 8509386..0af4f55 100644
--- a/src/menu/components/card/cardcomponent/index.jsx
+++ b/src/menu/components/card/cardcomponent/index.jsx
@@ -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' : ''
+      if (checkAll && cards.wrap.selStyle === 'check square') {
+        checkAll = ' mk-checkable square'
+      }
+    } 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/double-data-card/index.jsx b/src/menu/components/card/double-data-card/index.jsx
index 5ea6032..98cddef 100644
--- a/src/menu/components/card/double-data-card/index.jsx
+++ b/src/menu/components/card/double-data-card/index.jsx
@@ -63,11 +63,11 @@
         search: [],
         subcards: [{
           uuid: Utils.getuuid(),
-          setting: { width: 24 },
+          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', marginBottom: '8px'
+            marginLeft: '8px', marginRight: '8px', marginTop: '8px'
           },
           elements: [{
             uuid: Utils.getuuid(),
@@ -76,12 +76,13 @@
             width: 12,
             value: '寰幆鍖哄煙'
           }],
-          backSetting: {},
+          backSetting: {width: 24},
           backStyle: {
             borderLeftWidth: '1px', borderLeftColor: '#e8e8e8',
             borderRightWidth: '1px', borderRightColor: '#e8e8e8',
             borderBottomWidth: '1px', borderBottomColor: '#e8e8e8',
-            paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px'
+            paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
+            marginLeft: '8px', marginRight: '8px', marginBottom: '8px'
           },
           backElements: [{
             uuid: Utils.getuuid(),
@@ -175,6 +176,10 @@
       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) {
diff --git a/src/menu/components/card/double-data-card/options.jsx b/src/menu/components/card/double-data-card/options.jsx
index 2a7fabe..ae1d112 100644
--- a/src/menu/components/card/double-data-card/options.jsx
+++ b/src/menu/components/card/double-data-card/options.jsx
@@ -3,13 +3,13 @@
  */
 export default function (wrap, columns = [], setting) {
   let appType = sessionStorage.getItem('appType')
-  let MenuType = ''
-  let menu = window.GLOB.customMenu
+  // let MenuType = ''
+  // let menu = window.GLOB.customMenu
   let laypage = setting && setting.laypage !== 'false'
 
-  if (menu.parentId === 'BillPrintTemp') {
-    MenuType = 'billPrint'
-  }
+  // if (menu.parentId === 'BillPrintTemp') {
+  //   MenuType = 'billPrint'
+  // }
 
   let roleList = sessionStorage.getItem('sysRoles')
 
@@ -65,22 +65,22 @@
       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: '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',
@@ -144,19 +144,19 @@
         {value: 'check square', label: '鍕鹃�夛紙鏂规锛�'}
       ]
     },
-    {
-      type: 'radio',
-      field: 'cardFloat',
-      label: '瀵归綈鏂瑰紡',
-      initval: wrap.cardFloat || 'left',
-      tooltip: '璁剧疆鍗$墖鐨勫榻愭柟寮忋��',
-      required: false,
-      options: [
-        {value: 'left', label: '宸﹀榻�'},
-        {value: 'center', label: '灞呬腑'},
-        {value: 'right', label: '鍙冲榻�'},
-      ],
-    },
+    // {
+    //   type: 'radio',
+    //   field: 'cardFloat',
+    //   label: '瀵归綈鏂瑰紡',
+    //   initval: wrap.cardFloat || 'left',
+    //   tooltip: '璁剧疆鍗$墖鐨勫榻愭柟寮忋��',
+    //   required: false,
+    //   options: [
+    //     {value: 'left', label: '宸﹀榻�'},
+    //     {value: 'center', label: '灞呬腑'},
+    //     {value: 'right', label: '鍙冲榻�'},
+    //   ],
+    // },
     {
       type: 'radio',
       field: 'parity',
@@ -169,15 +169,15 @@
         {value: 'true', label: '鏈�'},
       ],
     },
-    {
-      type: 'number',
-      field: 'printHeight',
-      label: '鎹㈢畻楂樺害',
-      initval: wrap.printHeight || '',
-      tooltip: '褰撳墠鏁版嵁鍗¢珮搴︾浉褰撲簬鍑犳潯鏁版嵁銆�',
-      required: false,
-      forbid: MenuType !== 'billPrint'
-    },
+    // {
+    //   type: 'number',
+    //   field: 'printHeight',
+    //   label: '鎹㈢畻楂樺害',
+    //   initval: wrap.printHeight || '',
+    //   tooltip: '褰撳墠鏁版嵁鍗¢珮搴︾浉褰撲簬鍑犳潯鏁版嵁銆�',
+    //   required: false,
+    //   forbid: MenuType !== 'billPrint'
+    // },
     {
       type: 'radio',
       field: 'empty',
diff --git a/src/menu/components/card/doublecardcomponent/index.jsx b/src/menu/components/card/doublecardcomponent/index.jsx
index d79d883..86cdc9a 100644
--- a/src/menu/components/card/doublecardcomponent/index.jsx
+++ b/src/menu/components/card/doublecardcomponent/index.jsx
@@ -2,7 +2,7 @@
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
 import { Popover, Col } from 'antd'
-import { PlusOutlined, PlusSquareOutlined, EditOutlined, ToolOutlined, FontColorsOutlined } from '@ant-design/icons'
+import { UpOutlined, PlusOutlined, PlusSquareOutlined, EditOutlined, ToolOutlined, FontColorsOutlined } from '@ant-design/icons'
 
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
@@ -136,6 +136,7 @@
   }
 
   getSettingForms = () => {
+    const { cards } = this.props
     const { card } = this.state
 
     let buttons = []
@@ -148,10 +149,11 @@
       }
     })
 
-    return getSettingForm(card.setting, buttons)
+    return getSettingForm(card.setting, buttons, cards.columns, 'main')
   }
 
   getBackSettingForms = () => {
+    const { cards } = this.props
     const { card } = this.state
 
     let buttons = []
@@ -164,7 +166,7 @@
       }
     })
 
-    return getSettingForm(card.backSetting, buttons)
+    return getSettingForm(card.backSetting, buttons, cards.subColumns)
   }
 
   updateSetting = (res, type) => {
@@ -239,20 +241,32 @@
 
     let _style = {...card.style}
     let _backStyle = {...card.backStyle}
-
-    _backStyle.marginLeft = _style.marginLeft
-    _backStyle.marginRight = _style.marginRight
-    _backStyle.marginBottom = _style.marginBottom
-    
-    delete _style.marginBottom
+    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 = ''
+    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 || 24}>
-        <div className="card-item-wrap">
-          <div className={'card-item ' + (card.setting.btnControl || '')} style={_style} onDoubleClick={(e) => {e.stopPropagation(); this.doubleClickCard()}} id={card.uuid}>
+        <div className="card-item-wrap" style={_wrapStyle}>
+          <div className={`card-item ${card.setting.btnControl || ''} ${checkAll} mk-${card.setting.display}`} 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">
@@ -271,25 +285,27 @@
             </div>
             <CardCellComponent cards={cards} cardCell={card} side="main" elements={card.elements} updateElement={(elements, btn) => this.updateCard(elements, btn)}/>
           </div>
-          <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>
+          <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>
-            <CardCellComponent cards={cards} cardCell={card} side="sub" elements={card.backElements} updateElement={(elements, btn) => this.updateCard(elements, btn, 'sub')}/>
-          </div>
+          </Col>
         </div>
       </Col>
     )
diff --git a/src/menu/components/card/doublecardcomponent/index.scss b/src/menu/components/card/doublecardcomponent/index.scss
index a32dfaa..93f72bf 100644
--- a/src/menu/components/card/doublecardcomponent/index.scss
+++ b/src/menu/components/card/doublecardcomponent/index.scss
@@ -62,3 +62,50 @@
     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 5px;
+    font-size: 18px;
+  }
+}
+// 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;
+  }
+}
+.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
index 1a8a39c..16bd37f 100644
--- a/src/menu/components/card/doublecardcomponent/options.jsx
+++ b/src/menu/components/card/doublecardcomponent/options.jsx
@@ -1,7 +1,7 @@
 /**
  * @description Setting琛ㄥ崟閰嶇疆淇℃伅
  */
-export default function (setting, buttons = []) {
+export default function (setting, buttons = [], columns, type) {
   let appType = sessionStorage.getItem('appType')
   let menulist = []
 
@@ -43,6 +43,57 @@
     },
     {
       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 || '',
@@ -51,7 +102,8 @@
         {value: '', label: '鏃�'},
         {value: 'menu', label: '鑿滃崟'},
         {value: 'link', label: '閾炬帴'},
-        {value: 'button', label: '鎸夐挳'}
+        {value: 'button', label: '鎸夐挳'},
+        {value: 'unfold', label: '瀛愯〃缂╂斁', disabled: type !== 'main'},
       ],
       controlFields: [
         {field: 'menu', values: ['menu']},
@@ -59,7 +111,7 @@
         {field: 'open', values: ['menu', 'link']},
         {field: 'joint', values: ['menu', 'link']},
         {field: 'linkbtn', values: ['button']},
-        {field: 'clickType', values: ['button']},
+        {field: 'clickType', values: ['button', 'unfold']},
       ]
     },
     {
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/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/datasource/verifycard/settingform/index.jsx b/src/menu/datasource/verifycard/settingform/index.jsx
index cd21428..302af58 100644
--- a/src/menu/datasource/verifycard/settingform/index.jsx
+++ b/src/menu/datasource/verifycard/settingform/index.jsx
@@ -589,6 +589,21 @@
                 )}
               </Form.Item>
             </Col> : null}
+            {config.subtype === 'dualdatacard' ? <Col span={8}>
+              <Form.Item label="瀛愯〃BID">
+                {getFieldDecorator('subBID', {
+                  initialValue: setting.subBID || ''
+                })(
+                  <Select>
+                    {columns.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/modulesource/option.jsx b/src/menu/modulesource/option.jsx
index 92437f0..5c54bda 100644
--- a/src/menu/modulesource/option.jsx
+++ b/src/menu/modulesource/option.jsx
@@ -40,7 +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: 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'] },
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/modulesource/option.jsx b/src/mob/modulesource/option.jsx
index 1fc604b..1bd0577 100644
--- a/src/mob/modulesource/option.jsx
+++ b/src/mob/modulesource/option.jsx
@@ -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 },
diff --git a/src/pc/modulesource/option.jsx b/src/pc/modulesource/option.jsx
index db77df6..2391993 100644
--- a/src/pc/modulesource/option.jsx
+++ b/src/pc/modulesource/option.jsx
@@ -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 },
diff --git a/src/tabviews/custom/components/card/cardItem/index.jsx b/src/tabviews/custom/components/card/cardItem/index.jsx
index f6cb62f..d02f496 100644
--- a/src/tabviews/custom/components/card/cardItem/index.jsx
+++ b/src/tabviews/custom/components/card/cardItem/index.jsx
@@ -147,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) {
diff --git a/src/tabviews/custom/components/card/data-card/index.jsx b/src/tabviews/custom/components/card/data-card/index.jsx
index cbb6d55..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') {
@@ -1012,7 +992,5 @@
     )
   }
 }
-
-
 
 export default DataCard
\ No newline at end of file
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..b3ee79f
--- /dev/null
+++ b/src/tabviews/custom/components/card/double-data-card/index.jsx
@@ -0,0 +1,954 @@
+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 = {
+    data: PropTypes.array,           // 缁熶竴鏌ヨ鏁版嵁
+    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 () {
+    let _config = fromJS(this.props.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 = ''
+    if (config.wrap.selStyle && config.wrap.selStyle.indexOf('check') > -1) {
+      if (selectedData.length > 0) {
+        checkAll = selectedData.length < data.length ? ' half' : ' whole'
+      }
+    }
+
+    return (
+      <div className="custom-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 `
+              let subClass = 'mk-unfold'
+              let unfold = true
+              if (config.wrap.parity === 'true') {
+                if (index % 2 === 1) {
+                  className += 'mk-parity-bg '
+                }
+              }
+              if (item.$disabled) {
+                className = 'mk-disabled'
+              } 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.display !== 'default' && 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.display !== 'default' && card.setting.controlIcon !== 'left' ? <UpOutlined className={subClass} onClick={(e) => this.changeUnfold(e, index, subClass)}/> : null}
+                    </CardItem>
+                    <div className={'sub-card-wrap ' + subClass}>
+                      {item.children.map((cell, index) => <Col key={'d' + 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..6d9583a
--- /dev/null
+++ b/src/tabviews/custom/components/card/double-data-card/index.scss
@@ -0,0 +1,308 @@
+.custom-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;
+          }
+        }
+      }
+    }
+    .mk-parity-bg {
+      .card-item-box {
+        background-color: var(--mk-sys-color1);
+      }
+    }
+  }
+  .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;
+    }
+  }
+  .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;
+      width: 8px;
+      height: 8px;
+      background: var(--mk-sys-color);
+    }
+    .card-item-box {
+      // width: 100%;
+      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 5px;
+        font-size: 18px;
+      }
+      >.anticon-plus-square.mk-disabled {
+        color: #e8e8e8;
+        cursor: not-allowed;
+      }
+      >.anticon-minus-square {
+        margin: auto 5px;
+        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;
+    }
+  }
+}
+.custom-data-card-box::-webkit-scrollbar {
+  width: 7px;
+  height: 7px;
+}
+.custom-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);
+}
+.custom-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);
+}
+.custom-card-box::after {
+  content: ' ';
+  display: block;
+  clear: both;
+}
diff --git a/src/tabviews/custom/components/card/prop-card/index.jsx b/src/tabviews/custom/components/card/prop-card/index.jsx
index 531d10a..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'
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/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/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..dd41b85 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,
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/utils/utils-datamanage.js b/src/utils/utils-datamanage.js
index 29651eb..59bca43 100644
--- a/src/utils/utils-datamanage.js
+++ b/src/utils/utils-datamanage.js
@@ -276,6 +276,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/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 => {

--
Gitblit v1.8.0