From 2e3d8e7d5715862733e43070e7df73b48a81948f Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期三, 28 七月 2021 23:49:55 +0800
Subject: [PATCH] 2021-07-28

---
 src/pc/components/navbar/normal-navbar/index.jsx                  |    7 
 src/mob/components/navbar/normal-navbar/index.jsx                 |    5 
 src/menu/components/card/table-card/index.jsx                     |    2 
 src/mob/components/tabs/antv-tabs/index.jsx                       |  408 ++++++++++++
 src/menu/components/share/actioncomponent/dragaction/card.jsx     |    1 
 src/mob/mobshell/index.jsx                                        |    1 
 src/menu/components/card/cardcellcomponent/dragaction/action.jsx  |    4 
 src/menu/components/card/cardcellcomponent/elementform/index.jsx  |   22 
 src/menu/components/form/normal-form/index.jsx                    |    2 
 src/pc/menushell/index.jsx                                        |    1 
 src/tabviews/rolemanage/index.scss                                |   36 +
 src/menu/components/chart/antv-bar/index.jsx                      |    2 
 src/menu/components/chart/antv-scatter/index.jsx                  |    2 
 src/menu/components/code/sandbox/index.jsx                        |    2 
 src/mob/components/tabs/tabcomponents/index.jsx                   |  172 +++++
 src/menu/components/tree/antd-tree/index.jsx                      |    2 
 src/menu/components/carousel/cardcomponent/index.jsx              |    3 
 src/mob/mobshell/card.jsx                                         |    2 
 src/menu/components/carousel/data-card/index.jsx                  |    2 
 src/mob/components/tabs/antv-tabs/index.scss                      |  122 +++
 src/menu/components/card/balcony/index.jsx                        |    2 
 src/mob/components/menubar/normal-menubar/menucomponent/index.jsx |    4 
 src/menu/stylecontroller/index.jsx                                |   68 ++
 src/tabviews/rolemanage/index.jsx                                 |  670 ++++++++++++++++----
 src/mob/components/menubar/normal-menubar/index.jsx               |    4 
 src/menu/components/carousel/prop-card/index.jsx                  |    2 
 src/mob/components/tabs/tabcomponents/card.jsx                    |  105 +++
 src/menu/components/card/cardcomponent/index.jsx                  |    4 
 src/menu/components/card/data-card/index.jsx                      |    2 
 src/menu/components/chart/antv-pie/index.jsx                      |    2 
 src/menu/components/search/main-search/index.jsx                  |    2 
 src/menu/components/card/cardcellcomponent/formconfig.jsx         |   38 -
 src/menu/components/chart/antv-dashboard/index.jsx                |    2 
 src/mob/components/topbar/normal-navbar/index.jsx                 |    7 
 src/menu/picturecontroller/editform/index.jsx                     |    2 
 src/menu/components/editor/braft-editor/index.jsx                 |    2 
 src/menu/components/card/prop-card/index.jsx                      |    2 
 src/menu/components/table/normal-table/index.jsx                  |    2 
 src/menu/components/tabs/antv-tabs/index.jsx                      |    2 
 src/menu/components/group/normal-group/index.jsx                  |    2 
 src/mob/components/tabs/antv-tabs/dragabletabs.jsx                |  129 ++++
 src/mob/components/tabs/tabcomponents/index.scss                  |   14 
 42 files changed, 1,628 insertions(+), 237 deletions(-)

diff --git a/src/menu/components/card/balcony/index.jsx b/src/menu/components/card/balcony/index.jsx
index 0fc02d5..9b2971a 100644
--- a/src/menu/components/card/balcony/index.jsx
+++ b/src/menu/components/card/balcony/index.jsx
@@ -128,7 +128,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/card/cardcellcomponent/dragaction/action.jsx b/src/menu/components/card/cardcellcomponent/dragaction/action.jsx
index 4ce1876..2eb2c8e 100644
--- a/src/menu/components/card/cardcellcomponent/dragaction/action.jsx
+++ b/src/menu/components/card/cardcellcomponent/dragaction/action.jsx
@@ -41,9 +41,9 @@
   if (card.show === 'icon') {
     btnElement = (<Button style={_style} type="link"><Icon type={card.icon}/></Button>)
   } else if (card.show === 'link') {
-    btnElement = (<Button style={_style} type="link">{card.icon ? <Icon type={card.icon}/> : null}{card.label}</Button>)
+    btnElement = (<Button style={_style} type="link">{card.label}{card.icon ? <Icon type={card.icon}/> : null}</Button>)
   } else {
-    btnElement = (<Button style={_style}> {card.label}{card.icon ? <Icon type={card.icon}/> : null} </Button>)
+    btnElement = (<Button style={_style}> {card.icon ? <Icon type={card.icon}/> : null}{card.label} </Button>)
   }
 
   return (
diff --git a/src/menu/components/card/cardcellcomponent/elementform/index.jsx b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
index 38de87f..874d82a 100644
--- a/src/menu/components/card/cardcellcomponent/elementform/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -10,6 +10,7 @@
 const { TextArea } = Input
 const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
 const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
+const MkIcon = asyncComponent(() => import('@/components/mkIcon'))
 
 const cardTypeOptions = {
   sequence: ['eleType', 'width'],
@@ -353,6 +354,27 @@
             </Form.Item>
           </Col>
         )
+      } else if (item.type === 'icon') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal || '',
+                rules: [{
+                  required: !!item.required,
+                  message: this.props.dict['form.required.select'] + item.label + '!'
+                }]
+              })(
+                <MkIcon />
+              )}
+            </Form.Item>
+          </Col>
+        )
       } else if (item.type === 'radio') {
         fields.push(
           <Col span={12} key={index}>
diff --git a/src/menu/components/card/cardcellcomponent/formconfig.jsx b/src/menu/components/card/cardcellcomponent/formconfig.jsx
index ac0423d..526d6e6 100644
--- a/src/menu/components/card/cardcellcomponent/formconfig.jsx
+++ b/src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -63,45 +63,11 @@
       options: _options
     },
     {
-      type: 'select',
+      type: 'icon',
       key: 'icon',
       label: '鍥炬爣',
       initVal: card.icon,
-      required: true,
-      options: [
-        { value: 'question-circle', text: 'question-circle'},
-        { value: 'alert', text: 'alert'},
-        { value: 'cloud', text: 'cloud'},
-        { value: 'eye', text: 'eye'},
-        { value: 'eye-invisible', text: 'eye-invisible'},
-        { value: 'android', text: 'android'},
-        { value: 'apple', text: 'apple'},
-        { value: 'windows', text: 'windows'},
-        { value: 'ie', text: 'ie'},
-        { value: 'chrome', text: 'chrome'},
-        { value: 'github', text: 'github'},
-        { value: 'aliwangwang', text: 'aliwangwang'},
-        { value: 'dingding', text: 'dingding'},
-        { value: 'wechat', text: 'wechat'},
-        { value: 'alipay', text: 'alipay'},
-        { value: 'weibo-square', text: 'weibo-square'},
-        { value: 'weibo-circle', text: 'weibo-circle'},
-        { value: 'taobao-circle', text: 'taobao-circle'},
-        { value: 'weibo', text: 'weibo'},
-        { value: 'twitter', text: 'twitter'},
-        { value: 'youtube', text: 'youtube'},
-        { value: 'alipay-circle', text: 'alipay-circle'},
-        { value: 'taobao', text: 'taobao'},
-        { value: 'skype', text: 'skype'},
-        { value: 'qq', text: 'qq'},
-        { value: 'gitlab', text: 'gitlab'},
-        { value: 'zhihu', text: 'zhihu'},
-        { value: 'slack', text: 'slack'},
-        { value: 'sketch', text: 'sketch'},
-        { value: 'yahoo', text: 'yahoo'},
-        { value: 'reddit', text: 'reddit'},
-        { value: 'dribbble', text: 'dribbble'},
-      ]
+      required: true
     },
     {
       type: 'radio',
diff --git a/src/menu/components/card/cardcomponent/index.jsx b/src/menu/components/card/cardcomponent/index.jsx
index af376d8..84a1b6d 100644
--- a/src/menu/components/card/cardcomponent/index.jsx
+++ b/src/menu/components/card/cardcomponent/index.jsx
@@ -235,10 +235,6 @@
 
     let _style = {...card.style}
 
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
-    }
-
     if (side === 'back') {
       _style = {
         ...card.backStyle,
diff --git a/src/menu/components/card/data-card/index.jsx b/src/menu/components/card/data-card/index.jsx
index 5c85b95..cb8b128 100644
--- a/src/menu/components/card/data-card/index.jsx
+++ b/src/menu/components/card/data-card/index.jsx
@@ -236,7 +236,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/card/prop-card/index.jsx b/src/menu/components/card/prop-card/index.jsx
index e132a91..82de7bd 100644
--- a/src/menu/components/card/prop-card/index.jsx
+++ b/src/menu/components/card/prop-card/index.jsx
@@ -238,7 +238,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/card/table-card/index.jsx b/src/menu/components/card/table-card/index.jsx
index a7d5efe..5f34e5c 100644
--- a/src/menu/components/card/table-card/index.jsx
+++ b/src/menu/components/card/table-card/index.jsx
@@ -210,7 +210,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/carousel/cardcomponent/index.jsx b/src/menu/components/carousel/cardcomponent/index.jsx
index 1519f58..51cb8e0 100644
--- a/src/menu/components/carousel/cardcomponent/index.jsx
+++ b/src/menu/components/carousel/cardcomponent/index.jsx
@@ -172,9 +172,6 @@
 
     let _style = {...card.style}
 
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
-    }
     _style.height = cards.style.height
     _style = resetStyle(_style)
 
diff --git a/src/menu/components/carousel/data-card/index.jsx b/src/menu/components/carousel/data-card/index.jsx
index 9a3ab4f..75d24c5 100644
--- a/src/menu/components/carousel/data-card/index.jsx
+++ b/src/menu/components/carousel/data-card/index.jsx
@@ -201,7 +201,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/carousel/prop-card/index.jsx b/src/menu/components/carousel/prop-card/index.jsx
index 404d434..6459a3d 100644
--- a/src/menu/components/carousel/prop-card/index.jsx
+++ b/src/menu/components/carousel/prop-card/index.jsx
@@ -218,7 +218,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/chart/antv-bar/index.jsx b/src/menu/components/chart/antv-bar/index.jsx
index 19cd40e..0b11823 100644
--- a/src/menu/components/chart/antv-bar/index.jsx
+++ b/src/menu/components/chart/antv-bar/index.jsx
@@ -1293,7 +1293,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/chart/antv-dashboard/index.jsx b/src/menu/components/chart/antv-dashboard/index.jsx
index 54f8e71..b9dea12 100644
--- a/src/menu/components/chart/antv-dashboard/index.jsx
+++ b/src/menu/components/chart/antv-dashboard/index.jsx
@@ -489,7 +489,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/chart/antv-pie/index.jsx b/src/menu/components/chart/antv-pie/index.jsx
index 593fcd5..e08e450 100644
--- a/src/menu/components/chart/antv-pie/index.jsx
+++ b/src/menu/components/chart/antv-pie/index.jsx
@@ -651,7 +651,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/chart/antv-scatter/index.jsx b/src/menu/components/chart/antv-scatter/index.jsx
index a002498..3adb118 100644
--- a/src/menu/components/chart/antv-scatter/index.jsx
+++ b/src/menu/components/chart/antv-scatter/index.jsx
@@ -324,7 +324,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/code/sandbox/index.jsx b/src/menu/components/code/sandbox/index.jsx
index e125817..153f425 100644
--- a/src/menu/components/code/sandbox/index.jsx
+++ b/src/menu/components/code/sandbox/index.jsx
@@ -116,7 +116,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/editor/braft-editor/index.jsx b/src/menu/components/editor/braft-editor/index.jsx
index cf83121..e492b90 100644
--- a/src/menu/components/editor/braft-editor/index.jsx
+++ b/src/menu/components/editor/braft-editor/index.jsx
@@ -115,7 +115,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/form/normal-form/index.jsx b/src/menu/components/form/normal-form/index.jsx
index eae27ac..4835ca7 100644
--- a/src/menu/components/form/normal-form/index.jsx
+++ b/src/menu/components/form/normal-form/index.jsx
@@ -185,7 +185,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/group/normal-group/index.jsx b/src/menu/components/group/normal-group/index.jsx
index a0ac08f..ac34bbd 100644
--- a/src/menu/components/group/normal-group/index.jsx
+++ b/src/menu/components/group/normal-group/index.jsx
@@ -101,7 +101,7 @@
   changeStyle = () => {
     const { group } = this.state
 
-    MKEmitter.emit('changeStyle', [group.uuid], ['background', 'border', 'padding', 'margin'], group.style)
+    MKEmitter.emit('changeStyle', [group.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], group.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/search/main-search/index.jsx b/src/menu/components/search/main-search/index.jsx
index 2de3892..d17f664 100644
--- a/src/menu/components/search/main-search/index.jsx
+++ b/src/menu/components/search/main-search/index.jsx
@@ -107,7 +107,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   /**
diff --git a/src/menu/components/share/actioncomponent/dragaction/card.jsx b/src/menu/components/share/actioncomponent/dragaction/card.jsx
index d0af55e..4a6e8b5 100644
--- a/src/menu/components/share/actioncomponent/dragaction/card.jsx
+++ b/src/menu/components/share/actioncomponent/dragaction/card.jsx
@@ -38,6 +38,7 @@
 
   let btnElement = null
   let _style = resetStyle(card.style)
+
   if (card.show === 'icon') {
     btnElement = (
       <Button
diff --git a/src/menu/components/table/normal-table/index.jsx b/src/menu/components/table/normal-table/index.jsx
index 4bd4dd7..160f512 100644
--- a/src/menu/components/table/normal-table/index.jsx
+++ b/src/menu/components/table/normal-table/index.jsx
@@ -221,7 +221,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/tabs/antv-tabs/index.jsx b/src/menu/components/tabs/antv-tabs/index.jsx
index 0cb7d7d..7d64282 100644
--- a/src/menu/components/tabs/antv-tabs/index.jsx
+++ b/src/menu/components/tabs/antv-tabs/index.jsx
@@ -130,7 +130,7 @@
   changeStyle = () => {
     const { tabs } = this.state
 
-    MKEmitter.emit('changeStyle', [tabs.uuid], ['background', 'border', 'padding', 'margin'], tabs.style)
+    MKEmitter.emit('changeStyle', [tabs.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], tabs.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/tree/antd-tree/index.jsx b/src/menu/components/tree/antd-tree/index.jsx
index 18f83e7..f392778 100644
--- a/src/menu/components/tree/antd-tree/index.jsx
+++ b/src/menu/components/tree/antd-tree/index.jsx
@@ -114,7 +114,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/picturecontroller/editform/index.jsx b/src/menu/picturecontroller/editform/index.jsx
index 934ebd7..c739488 100644
--- a/src/menu/picturecontroller/editform/index.jsx
+++ b/src/menu/picturecontroller/editform/index.jsx
@@ -82,7 +82,7 @@
               })(
                 <FileUpload config={{
                   initval: '',
-                  suffix: '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp',
+                  suffix: '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp,.ico',
                   maxfile: 1,
                   fileType: 'picture'
                 }} />
diff --git a/src/menu/stylecontroller/index.jsx b/src/menu/stylecontroller/index.jsx
index 69d11e7..a253fea 100644
--- a/src/menu/stylecontroller/index.jsx
+++ b/src/menu/stylecontroller/index.jsx
@@ -218,7 +218,44 @@
    * @description 淇敼闃村奖棰滆壊 锛岄鑹叉帶浠�
    */
   changeShadowColor = (val) => {
-    this.updateStyle({shadow: val})
+    const { card } = this.state
+
+    let boxShadow = `${card.hShadow || '0px'} ${card.vShadow || '0px'} ${card.shadowBlur || '0px'} ${val}`
+
+    this.updateStyle({shadowColor: val, boxShadow})
+  }
+
+  /**
+   * @description 淇敼闃村奖棰滆壊 锛岄鑹叉帶浠�
+   */
+  changeShadowBlur = (val) => {
+    const { card } = this.state
+
+    let boxShadow = `${card.hShadow || '0px'} ${card.vShadow || '0px'} ${val || '0px'} ${card.shadowColor || 'transparent'}`
+
+    this.updateStyle({shadowBlur: val, boxShadow})
+  }
+
+  /**
+   * @description 淇敼闃村奖棰滆壊 锛岄鑹叉帶浠�
+   */
+  changeHShadow = (val) => {
+    const { card } = this.state
+
+    let boxShadow = `${val || '0px'} ${card.vShadow || '0px'} ${card.shadowBlur || '0px'} ${card.shadowColor || 'transparent'}`
+
+    this.updateStyle({hShadow: val, boxShadow})
+  }
+
+  /**
+   * @description 淇敼闃村奖棰滆壊 锛岄鑹叉帶浠�
+   */
+  changeVShadow = (val) => {
+    const { card } = this.state
+
+    let boxShadow = `${card.hShadow || '0px'} ${val || '0px'} ${card.shadowBlur || '0px'} ${card.shadowColor || 'transparent'}`
+
+    this.updateStyle({vShadow: val, boxShadow})
   }
 
   imgChange = (val) => {
@@ -609,7 +646,34 @@
                     label={<Icon title="闃村奖棰滆壊" type="bg-colors" />}
                     labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                   >
-                    <ColorSketch value={card.shadow || 'transparent'} onChange={this.changeShadowColor} />
+                    <ColorSketch value={card.shadowColor || 'transparent'} onChange={this.changeShadowColor} />
+                  </Form.Item>
+                </Col>
+                <Col span={24}>
+                  <Form.Item
+                    colon={false}
+                    label={<Icon title="妯$硦璺濈" type="column-width" />}
+                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
+                  >
+                    <StyleInput defaultValue={card.shadowBlur || '0px'} options={['px']} onChange={this.changeShadowBlur}/>
+                  </Form.Item>
+                </Col>
+                <Col span={24}>
+                  <Form.Item
+                    colon={false}
+                    label={<Icon title="姘村钩浣嶇疆" type="arrow-right" />}
+                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
+                  >
+                    <StyleInput defaultValue={card.hShadow || '0px'} options={['px']} onChange={this.changeHShadow}/>
+                  </Form.Item>
+                </Col>
+                <Col span={24}>
+                  <Form.Item
+                    colon={false}
+                    label={<Icon title="鍨傜洿浣嶇疆" type="arrow-down" />}
+                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
+                  >
+                    <StyleInput defaultValue={card.vShadow || '0px'} options={['px']} onChange={this.changeVShadow}/>
                   </Form.Item>
                 </Col>
               </Panel> : null}
diff --git a/src/mob/components/menubar/normal-menubar/index.jsx b/src/mob/components/menubar/normal-menubar/index.jsx
index e02f32e..8603277 100644
--- a/src/mob/components/menubar/normal-menubar/index.jsx
+++ b/src/mob/components/menubar/normal-menubar/index.jsx
@@ -155,7 +155,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -242,7 +242,7 @@
 
     return (
       <div className="menu-menubar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
-        <NormalHeader config={card} updateComponent={this.updateComponent}/>
+        {card.wrap.title ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null}
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <Icon className="plus" title="娣诲姞鑿滃崟" onClick={this.addMenu} type="plus" />
diff --git a/src/mob/components/menubar/normal-menubar/menucomponent/index.jsx b/src/mob/components/menubar/normal-menubar/menucomponent/index.jsx
index b85cfd5..34bf6db 100644
--- a/src/mob/components/menubar/normal-menubar/menucomponent/index.jsx
+++ b/src/mob/components/menubar/normal-menubar/menucomponent/index.jsx
@@ -153,10 +153,6 @@
 
     let _style = {...card.style}
 
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
-    }
-
     _style = resetStyle(_style)
 
     return (
diff --git a/src/mob/components/navbar/normal-navbar/index.jsx b/src/mob/components/navbar/normal-navbar/index.jsx
index 3565e4a..0d38e64 100644
--- a/src/mob/components/navbar/normal-navbar/index.jsx
+++ b/src/mob/components/navbar/normal-navbar/index.jsx
@@ -118,7 +118,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['font', 'background', 'border', 'padding'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['font', 'background', 'border', 'padding', 'shadow'], card.style)
   }
 
   clickComponent = (e) => {
@@ -145,9 +145,6 @@
     const { card } = this.state
 
     let _style = {...card.style}
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
-    }
     _style.height = card.wrap.height
 
     return (
diff --git a/src/mob/components/tabs/antv-tabs/dragabletabs.jsx b/src/mob/components/tabs/antv-tabs/dragabletabs.jsx
new file mode 100644
index 0000000..b2d7ef7
--- /dev/null
+++ b/src/mob/components/tabs/antv-tabs/dragabletabs.jsx
@@ -0,0 +1,129 @@
+import React, { Component } from 'react'
+import { Tabs } from 'antd'
+import { is, fromJS } from 'immutable'
+import { DndProvider, DragSource, DropTarget } from 'react-dnd'
+
+// Drag & Drop node
+class TabNode extends Component {
+  render() {
+    const { connectDragSource, connectDropTarget, children } = this.props
+
+    return connectDragSource(connectDropTarget(children))
+  }
+}
+
+const cardTarget = {
+  drop(props, monitor) {
+    const dragKey = monitor.getItem().index
+    const hoverKey = props.index
+
+    if (dragKey === hoverKey) {
+      return;
+    }
+
+    props.moveTabNode(dragKey, hoverKey)
+    monitor.getItem().index = hoverKey
+  }
+}
+
+const cardSource = {
+  beginDrag(props) {
+    return {
+      id: props.id,
+      index: props.index,
+    }
+  }
+}
+
+const WrapTabNode = DropTarget('DND_NODE', cardTarget, connect => ({
+  connectDropTarget: connect.dropTarget(),
+}))(
+  DragSource('DND_NODE', cardSource, (connect, monitor) => ({
+    connectDragSource: connect.dragSource(),
+    isDragging: monitor.isDragging(),
+  }))(TabNode)
+)
+
+class DraggableTabs extends Component {
+  state = {
+    order: []
+  }
+
+  moveTabNode = (dragKey, hoverKey) => {
+    const newOrder = this.state.order.slice()
+    const { children } = this.props
+
+    React.Children.forEach(children, c => {
+      if (newOrder.indexOf(c.key) === -1) {
+        newOrder.push(c.key)
+      }
+    })
+
+    const dragIndex = newOrder.indexOf(dragKey)
+    const hoverIndex = newOrder.indexOf(hoverKey)
+
+    newOrder.splice(dragIndex, 1)
+    newOrder.splice(hoverIndex, 0, dragKey)
+    
+    this.setState({
+      order: newOrder
+    })
+    this.props.tabsMove(newOrder)
+  }
+
+  renderTabBar = (props, DefaultTabBar) => (
+    <DefaultTabBar {...props}>
+      {node => (
+        <WrapTabNode key={node.key} index={node.key} moveTabNode={this.moveTabNode}>
+          {node}
+        </WrapTabNode>
+      )}
+    </DefaultTabBar>
+  )
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState)) ||
+      !is(fromJS(nextProps.children), fromJS(this.props.children)) ||
+      nextProps.tabPosition !== this.props.tabPosition ||
+      nextProps.type !== this.props.type
+  }
+
+  render() {
+    const { order } = this.state
+    const { children } = this.props
+    const tabs = []
+    React.Children.forEach(children, c => {
+      tabs.push(c)
+    })
+
+    const orderTabs = tabs.slice().sort((a, b) => {
+      const orderA = order.indexOf(a.key)
+      const orderB = order.indexOf(b.key)
+
+      if (orderA !== -1 && orderB !== -1) {
+        return orderA - orderB
+      }
+      if (orderA !== -1) {
+        return -1
+      }
+      if (orderB !== -1) {
+        return 1
+      }
+
+      const ia = tabs.indexOf(a)
+      const ib = tabs.indexOf(b)
+
+      return ia - ib
+    })
+
+    return (
+      <DndProvider>
+        <Tabs renderTabBar={this.renderTabBar} {...this.props}>
+          {orderTabs}
+        </Tabs>
+      </DndProvider>
+    )
+  }
+}
+
+export default DraggableTabs
\ No newline at end of file
diff --git a/src/mob/components/tabs/antv-tabs/index.jsx b/src/mob/components/tabs/antv-tabs/index.jsx
new file mode 100644
index 0000000..b98c70c
--- /dev/null
+++ b/src/mob/components/tabs/antv-tabs/index.jsx
@@ -0,0 +1,408 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Tabs, Icon, Popover, Modal } from 'antd'
+
+import MKEmitter from '@/utils/events.js'
+import asyncComponent from '@/utils/asyncComponent'
+import asyncIconComponent from '@/utils/asyncIconComponent'
+import DraggableTabs from './dragabletabs'
+import { resetStyle } from '@/utils/utils-custom.js'
+import MenuUtils from '@/utils/utils-custom.js'
+import Utils from '@/utils/utils.js'
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import './index.scss'
+
+const SettingComponent = asyncIconComponent(() => import('@/menu/components/tabs/tabsetting'))
+const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const PasteController = asyncIconComponent(() => import('@/menu/pastecontroller'))
+const TabLabelComponent = asyncComponent(() => import('@/menu/components/tabs/tablabelform'))
+const TabComponents = asyncComponent(() => import('../tabcomponents'))
+
+const { TabPane } = Tabs
+const { confirm } = Modal
+
+class antvTabs extends Component {
+  static propTpyes = {
+    tabs: PropTypes.object,
+    deletecomponent: PropTypes.func,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    appType: sessionStorage.getItem('appType'),
+    tabs: null,
+    editab: null,
+    labelvisible: false
+  }
+
+  UNSAFE_componentWillMount () {
+    const { tabs } = this.props
+
+    if (tabs.isNew) {
+      let _tabs = {
+        uuid: tabs.uuid,
+        type: tabs.type,
+        floor: tabs.floor,
+        tabId: tabs.tabId || '',
+        parentId: tabs.parentId || '',
+        subtype: tabs.subtype,
+        width: 24,
+        name: tabs.name,
+        setting: {width: 24, position: 'top', tabStyle: 'line', name: tabs.name},
+        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' },
+        subtabs: [
+          { uuid: Utils.getuuid(), parentId: tabs.uuid, floor: tabs.floor, label: 'Tab 1', icon: '', components: [] },
+          { uuid: Utils.getuuid(), parentId: tabs.uuid, floor: tabs.floor, label: 'Tab 2', icon: '', components: [] },
+          { uuid: Utils.getuuid(), parentId: tabs.uuid, floor: tabs.floor, label: 'Tab 3', icon: '', components: [] }
+        ]
+      }
+      this.setState({
+        tabs: _tabs
+      })
+      this.props.updateConfig(_tabs)
+    } else {
+      this.setState({
+        tabs: fromJS(tabs).toJS()
+      })
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('submitStyle', this.getStyle)
+    MKEmitter.addListener('submitSearch', this.getSearch)
+    MKEmitter.addListener('tabsChange', this.handleTabsChange)
+    MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('submitStyle', this.getStyle)
+    MKEmitter.removeListener('submitSearch', this.getSearch)
+    MKEmitter.removeListener('tabsChange', this.handleTabsChange)
+    MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
+  }
+
+  updateComponentStyle = (parentId, keys, style) => {
+    const { tabs } = this.state
+
+    if (tabs.subtabs.findIndex(tab => tab.uuid === parentId) === -1) return
+
+    let _tabs = fromJS(tabs).toJS()
+    let _tabs_ = fromJS(tabs).toJS()
+
+    let components = []
+    _tabs.subtabs.forEach(tab => {
+      if (tab.uuid === parentId) {
+        components = tab.components.map(item => {
+          if (keys.includes(item.uuid)) {
+            item.style = {...item.style, ...style}
+          }
+          return item
+        })
+        tab.components = []
+      }
+    })
+
+    _tabs_.subtabs = _tabs_.subtabs.map(tab => {
+      if (tab.uuid === parentId) {
+        tab.components = components
+      }
+      return tab
+    })
+
+    this.setState({tabs: _tabs}, () => {
+      this.updateComponent(_tabs_)
+    })
+  }
+
+  changeStyle = () => {
+    const { tabs } = this.state
+
+    MKEmitter.emit('changeStyle', [tabs.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], tabs.style)
+  }
+
+  getStyle = (comIds, style) => {
+    const { tabs } = this.state
+
+    if (comIds.length !== 1 || comIds[0] !== tabs.uuid) return
+
+    let _card = {...tabs, style}
+
+    this.setState({
+      tabs: _card
+    })
+    
+    this.props.updateConfig(_card)
+  }
+
+  handleTabsChange = (parentId) => {
+    const { tabs } = this.state
+
+    if (parentId === tabs.parentId) {
+      MKEmitter.emit('tabsChange', tabs.uuid)
+    }
+  }
+
+  updateComponent = (component) => {
+    const { tabs } = this.state
+
+    if (!is(fromJS(tabs.setting), fromJS(component.setting)) || !is(fromJS(tabs.style), fromJS(component.style))) {
+      // 娉ㄥ唽浜嬩欢-鏍囩鍙樺寲锛岄�氱煡鏍囩鍐呭厓绱�
+      MKEmitter.emit('tabsChange', tabs.uuid)
+    }
+
+    component.width = component.setting.width
+    component.name = component.setting.name
+
+    this.setState({
+      tabs: component
+    })
+    this.props.updateConfig(component)
+  }
+
+  updateTabComponent = (tab) => {
+    let tabs = fromJS(this.state.tabs).toJS()
+
+    tabs.subtabs = tabs.subtabs.map(t => {
+      if (t.uuid === tab.uuid) {
+        return tab
+      } else {
+        return t
+      }
+    })
+
+    this.setState({tabs})
+    this.props.updateConfig(tabs)
+  }
+
+  tabAdd = (e) => {
+    const { tabs } = this.state
+
+    e.stopPropagation()
+
+    this.setState({
+      editab: {
+        uuid: '',
+        parentId: tabs.uuid,
+        floor: tabs.floor,
+        label: '',
+        icon: '',
+        components: []
+      },
+      labelvisible: true
+    })
+  }
+
+  editTab = (tab) => {
+    this.setState({
+      editab: tab,
+      labelvisible: true
+    })
+  }
+
+  tabLabelSubmit = () => {
+    let tabs = fromJS(this.state.tabs).toJS()
+    let editab = fromJS(this.state.editab).toJS()
+
+    this.tabLabelRef.handleConfirm().then(res => {
+      editab.label = res.label
+      editab.icon = res.icon
+      editab.hasSearch = res.hasSearch || ''
+      editab.blacklist = res.blacklist
+
+      if (editab.uuid) {
+        tabs.subtabs = tabs.subtabs.map(t => {
+          if (t.uuid === editab.uuid) {
+            return editab
+          } else {
+            return t
+          }
+        })
+      } else {
+        editab.uuid = Utils.getuuid()
+        tabs.subtabs.push(editab)
+      }
+
+      this.setState({
+        editab: null,
+        labelvisible: false,
+        tabs
+      })
+      this.props.updateConfig(tabs)
+    })
+  }
+
+  delTab = (tab) => {
+    let tabs = fromJS(this.state.tabs).toJS()
+    const _this = this
+
+    tabs.subtabs = tabs.subtabs.filter(t => t.uuid !== tab.uuid)
+
+    let uuids = MenuUtils.getDelButtonIds({...tab, type: 'group'})
+
+    confirm({
+      title: '纭畾鍒犻櫎鏍囩锛�',
+      content: '',
+      onOk() {
+        _this.setState({tabs})
+        _this.props.updateConfig(tabs)
+
+        if (uuids.length === 0) return
+        
+        MKEmitter.emit('delButtons', uuids)
+      },
+      onCancel() {}
+    })
+  }
+
+  moveSwitch = (order) => {
+    let tabs = fromJS(this.state.tabs).toJS()
+    let subtab = {}
+    tabs.subtabs.forEach(item => {
+      subtab[item.uuid] = item
+    })
+
+    tabs.subtabs = []
+
+    order.forEach(item => {
+      if (subtab[item]) {
+        tabs.subtabs.push(subtab[item])
+      }
+    })
+
+    this.setState({tabs})
+    this.props.updateConfig(tabs)
+  }
+
+  insert = (item, tab) => {
+    let tabs = fromJS(this.state.tabs).toJS()
+
+    tabs.subtabs.forEach(stab => {
+      if (stab.uuid === tab.uuid) {
+        stab.components.push(item)
+      }
+    })
+
+    this.setState({tabs})
+    this.props.updateConfig(tabs)
+  }
+
+  getSearch = (config) => {
+    const { tabs } = this.state
+
+    if (tabs.uuid !== config.uuid) return
+
+    let _tabs = fromJS(tabs).toJS()
+
+    _tabs.subtabs = _tabs.subtabs.map(t => {
+      if (t.uuid === config.tabId) {
+        t.search = config.search
+      }
+      return t
+    })
+
+    this.setState({
+      tabs: _tabs
+    })
+    this.props.updateConfig(_tabs)
+  }
+
+  setSearch = (tab) => {
+    const { tabs } = this.state
+    let card = {
+      uuid: tabs.uuid,
+      tabId: tab.uuid,
+      search: tab.search
+    }
+
+    if (!card.search) {
+      card.search = {
+        floor: 1,
+        setting: { type: 'title', field: '', title: '', focus: 'true', btn: 'hidden' },
+        groups: [],
+        fields: []
+      }
+    }
+    MKEmitter.emit('changeSearch', card)
+  }
+
+  clickComponent = (e) => {
+    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
+      e.stopPropagation()
+      MKEmitter.emit('clickComponent', this.state.tabs)
+    }
+  }
+
+  render() {
+    const { tabs, dict, labelvisible, editab, appType } = this.state
+    let _style = resetStyle(tabs.style)
+
+    return (
+      <div className={'menu-tabs-edit-box ' + tabs.setting.display} style={_style} onClick={this.clickComponent} id={tabs.uuid}>
+        <DraggableTabs tabPosition={tabs.setting.position} type={tabs.setting.tabStyle} tabsMove={this.moveSwitch}>
+          {tabs.subtabs.map(tab => (
+            <TabPane tab={
+              <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+                <div className="mk-popover-control">
+                  <Icon className="edit" title="edit" type="edit" onClick={() => this.editTab(tab)} />
+                  <PasteController type="tab" Tab={tab} insert={this.insert} />
+                  <Icon className="close" title="delete" type="close" onClick={() => this.delTab(tab)} />
+                </div>
+              } trigger="hover">
+                <span>{tab.icon ? <Icon type={tab.icon} /> : null}{tab.label}</span>
+              </Popover>
+            } key={tab.uuid}>
+              {appType === 'mob' && tabs.setting.position === 'top' && tabs.setting.display === 'inline-block' && tab.hasSearch === 'icon' ?
+                <Icon className="search-icon" onDoubleClick={() => this.setSearch(tab)} type="search" /> : null}
+              <TabComponents config={tab} handleList={this.updateTabComponent} deleteCard={this.deleteCard} />
+            </TabPane>
+          ))}
+        </DraggableTabs>
+        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+          <div className="mk-popover-control">
+            <Icon className="plus" title="娣诲姞鏍囩" type="plus" onClick={this.tabAdd} />
+            <SettingComponent config={tabs} updateConfig={this.updateComponent} />
+            <CopyComponent type="tabs" card={tabs}/>
+            <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
+            <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(tabs.uuid)} />
+          </div>
+        } trigger="hover">
+          <Icon type="tool" />
+        </Popover>
+        <Modal
+          wrapClassName="popview-modal"
+          title={'鏍囩缂栬緫'}
+          visible={labelvisible}
+          width={600}
+          maskClosable={false}
+          okText={dict['model.submit']}
+          onOk={this.tabLabelSubmit}
+          onCancel={() => { this.setState({ labelvisible: false }) }}
+          destroyOnClose
+        >
+          <TabLabelComponent
+            dict={dict}
+            tab={editab}
+            setting={tabs.setting}
+            inputSubmit={this.tabLabelSubmit}
+            wrappedComponentRef={(inst) => this.tabLabelRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default antvTabs
\ No newline at end of file
diff --git a/src/mob/components/tabs/antv-tabs/index.scss b/src/mob/components/tabs/antv-tabs/index.scss
new file mode 100644
index 0000000..7ad16c9
--- /dev/null
+++ b/src/mob/components/tabs/antv-tabs/index.scss
@@ -0,0 +1,122 @@
+.menu-tabs-edit-box {
+  position: relative;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+
+  >.anticon-tool {
+    position: absolute;
+    z-index: 2;
+    font-size: 16px;
+    right: 1px;
+    top: 1px;
+    cursor: pointer;
+    padding: 5px;
+    background: rgba(255, 255, 255, 0.55);
+  }
+
+  .ant-tabs.ant-tabs-left, .ant-tabs.ant-tabs-bottom {
+    .tab-shell-inner {
+      padding-top: 25px;
+    }
+  }
+
+  .ant-tabs-tabpane-active {
+    min-height: 200px;
+  }
+
+  .ant-tabs .ant-tabs-top-bar > .ant-tabs-nav-container {
+    >.ant-tabs-tab-next:not(.ant-tabs-tab-arrow-show) + .ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav {
+      width: 100%;
+      > div > .ant-tabs-tab-disabled {
+        float: right;
+      }
+    }
+  }
+  
+  .ant-tabs .ant-tabs-left-bar .ant-tabs-tab {
+    text-align: right;
+    > span {
+      display: inline-block;
+      padding: 8px 24px;
+    }
+  }
+  .ant-tabs .ant-tabs-right-bar .ant-tabs-tab {
+    text-align: left;
+    > span {
+      display: inline-block;
+      padding: 8px 24px;
+    }
+  }
+  .ant-tabs-tab {
+    padding: 0px!important;
+    text-align: center;
+    > span {
+      display: inline-block;
+      padding: 12px 16px;
+    }
+  }
+  .ant-tabs-bottom .ant-tabs-bottom-bar .ant-tabs-ink-bar {
+    top: 0px;
+  }
+  .ant-tabs-card {
+    .ant-tabs-left-bar, .ant-tabs-right-bar {
+      .ant-tabs-tab {
+        > span {
+          padding: 0px 16px;
+        }
+      }
+      .ant-tabs-tab-active {
+        padding-left: 0px!important;
+        padding-right: 0px!important;
+      }
+    }
+    
+    .ant-tabs-card-bar {
+      .ant-tabs-tab {
+        > span {
+          display: inline-block;
+          padding: 0px 16px;
+        }
+      }
+    }
+  }
+  .search-icon {
+    position: absolute;
+    top: 10px;
+    right: 40px;
+    font-size: 18px;
+    cursor: pointer;
+    padding: 3px;
+  }
+}
+.menu-tabs-edit-box:hover {
+  z-index: 1;
+  box-shadow: 0px 0px 4px #1890ff;
+}
+
+.mob-shell {
+  .menu-tabs-edit-box.flex {
+    >.ant-tabs.ant-tabs-top, >.ant-tabs.ant-tabs-bottom {
+      >.ant-tabs-bar {
+        >.ant-tabs-nav-container {
+          >.ant-tabs-nav-wrap {
+            >.ant-tabs-nav-scroll {
+              >.ant-tabs-nav {
+                display: block;
+                >div {
+                  display: flex;
+                  >.ant-tabs-tab {
+                    flex: 1;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/mob/components/tabs/tabcomponents/card.jsx b/src/mob/components/tabs/tabcomponents/card.jsx
new file mode 100644
index 0000000..24acf4d
--- /dev/null
+++ b/src/mob/components/tabs/tabcomponents/card.jsx
@@ -0,0 +1,105 @@
+import React from 'react'
+import { useDrag, useDrop } from 'react-dnd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const AntvBar = asyncComponent(() => import('@/menu/components/chart/antv-bar'))
+const MainSearch = asyncComponent(() => import('@/menu/components/search/main-search'))
+const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
+const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
+const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
+const AntvTabs = asyncComponent(() => import('@/mob/components/tabs/antv-tabs'))
+const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
+const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
+const NormalTree = asyncComponent(() => import('@/menu/components/tree/antd-tree'))
+const CarouselDataCard = asyncComponent(() => import('@/menu/components/carousel/data-card'))
+const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
+const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
+const NormalForm = asyncComponent(() => import('@/menu/components/form/normal-form'))
+const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
+const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
+const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
+const NormalMenuBar = asyncComponent(() => import('@/mob/components/menubar/normal-menubar'))
+const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
+
+const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
+  const originalIndex = findCard(id).index
+  const [{ isDragging }, drag] = useDrag({
+    item: { type: 'menu', id, originalIndex, floor: card.floor },
+    collect: monitor => ({
+      isDragging: monitor.isDragging(),
+    }),
+  })
+  const [, drop] = useDrop({
+    accept: 'menu',
+    canDrop: () => true,
+    drop: (item) => {
+      const { id: draggedId, originalIndex, floor } = item
+      if (originalIndex === undefined) {
+        item.dropTargetId = id
+      } else if (draggedId && floor === card.floor) {
+        if (draggedId === id) return
+        const { index: originIndex } = findCard(draggedId)
+
+        if (originIndex === -1) return
+
+        const { index: overIndex } = findCard(id)
+
+        moveCard(draggedId, overIndex)
+      }
+    }
+  })
+
+  let style = { opacity: 1}
+  if (isDragging) {
+    style = { opacity: 0.3}
+  }
+
+  const getCardComponent = () => {
+    if (card.type === 'bar' || card.type === 'line') {
+      return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
+    } else if (card.type === 'search') {
+      return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'pie') {
+      return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'dashboard') {
+      return (<AntvDashboard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'tree') {
+      return (<NormalTree card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'scatter') {
+      return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'form') {
+      return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'tabs') {
+      return (<AntvTabs tabs={card} updateConfig={updateConfig} deletecomponent={delCard} />)
+    } else if (card.type === 'card' && card.subtype === 'datacard') {
+      return (<DataCard card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
+    } else if (card.type === 'card' && card.subtype === 'propcard') {
+      return (<PropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'carousel' && card.subtype === 'datacard') {
+      return (<CarouselDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'carousel' && card.subtype === 'propcard') {
+      return (<CarouselPropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'table' && card.subtype === 'tablecard') {
+      return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'table' && card.subtype === 'normaltable') {
+      return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'group' && card.subtype === 'normalgroup') {
+      return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'editor') {
+      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'menubar') {
+      return (<NormalMenuBar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'code') {
+      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    }
+  }
+
+  return (
+    <div className={'ant-col mk-component-card ant-col-' + (card.width || 24)} ref={node => drag(drop(node))} style={style}>
+      {getCardComponent()}
+    </div>
+  )
+}
+export default Card
diff --git a/src/mob/components/tabs/tabcomponents/index.jsx b/src/mob/components/tabs/tabcomponents/index.jsx
new file mode 100644
index 0000000..2259bf2
--- /dev/null
+++ b/src/mob/components/tabs/tabcomponents/index.jsx
@@ -0,0 +1,172 @@
+import React, { useState } from 'react'
+import { useDrop } from 'react-dnd'
+import { is, fromJS } from 'immutable'
+import update from 'immutability-helper'
+import { Empty, notification, Modal } from 'antd'
+
+import Utils from '@/utils/utils.js'
+import MKEmitter from '@/utils/events.js'
+import MenuUtils from '@/utils/utils-custom.js'
+import Card from './card'
+import './index.scss'
+
+const { confirm } = Modal
+
+const Container = ({ config, handleList }) => {
+  const [cards, setCards] = useState(config.components)
+  const moveCard = (id, atIndex) => {
+    const { card, index } = findCard(id)
+    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
+    handleList({...config, components: _cards})
+  }
+
+  if (!is(fromJS(cards), fromJS(config.components))) {
+    setCards(config.components)
+  }
+  
+  const findCard = id => {
+    const card = cards.filter(c => `${c.uuid}` === id)[0]
+    return {
+      card,
+      index: cards.indexOf(card),
+    }
+  }
+
+  const updateConfig = (element) => {
+    handleList({...config, components: cards.map(item => item.uuid === element.uuid ? element : item)})
+  }
+
+  const deleteCard = (id) => {
+    const { card } = findCard(id)
+
+    let hasComponent = false
+    if (card.type === 'tabs') {
+      card.subtabs.forEach(tab => {
+        if (tab.components.length > 0) {
+          hasComponent = true
+        }
+      })
+    }
+
+    let uuids = MenuUtils.getDelButtonIds(card)
+
+    confirm({
+      title: `纭畾鍒犻櫎銆�${card.name}銆嬪悧锛焋,
+      content: hasComponent ? '褰撳墠缁勪欢涓惈鏈夊瓙缁勪欢锛�' : '',
+      onOk() {
+        handleList({...config, components: cards.filter(item => item.uuid !== card.uuid)})
+
+        if (uuids.length === 0) return
+        
+        MKEmitter.emit('delButtons', uuids)
+      },
+      onCancel() {}
+    })
+  }
+
+  const [, drop] = useDrop({
+    accept: 'menu',
+    drop(item) {
+      if (item.hasOwnProperty('originalIndex') || item.added) {
+        return
+      }
+      item.added = true
+
+      if (item.component === 'search') { // 鎼滅储缁勪欢涓嶅彲閲嶅娣诲姞
+        if (cards.filter(card => card.type === 'search').length > 0) {
+          notification.warning({
+            top: 92,
+            message: '鎼滅储鏉′欢涓嶅彲閲嶅娣诲姞锛�',
+            duration: 5
+          })
+          return
+        }
+      } else if (item.component === 'tabs' && config.floor === 3) {
+        notification.warning({
+          top: 92,
+          message: '鏍囩椤垫渶澶氫负涓夐噸缁撴瀯锛�',
+          duration: 5
+        })
+        return
+      }
+
+      let name = ''
+      let names = {
+        bbar: '鏌辩姸鍥�',
+        line: '鎶樼嚎鍥�',
+        tabs: '鏍囩缁�',
+        pie: '楗煎浘',
+        search: '鎼滅储',
+        table: '琛ㄦ牸',
+        group: '鍒嗙粍',
+        editor: '瀵屾枃鏈�',
+        code: '鑷畾涔�',
+        carousel: '杞挱',
+        form: '琛ㄥ崟',
+        dashboard: '浠〃鐩�',
+        scatter: '鏁g偣鍥�',
+        menubar: '鑿滃崟鏍�',
+        tree: '鏍戝舰鍒楄〃',
+        card: '鍗$墖'
+      }
+      let i = 1
+      
+      while (!name && names[item.component]) {
+        let _name = names[item.component] + i
+        if (config.components.filter(com => com.name === _name).length === 0) {
+          name = _name
+        }
+        i++
+      }
+      
+      let newcard = {
+        uuid: Utils.getuuid(),
+        tabId: config.uuid,
+        parentId: config.parentId,
+        type: item.component,
+        subtype: item.subtype,
+        config: item.config,
+        width: item.width || 24,
+        dataName: Utils.getdataName(),
+        name: name,
+        floor: config.floor ? (config.floor + 1) : 2, // 缁勪欢鐨勫眰绾�
+        isNew: true                                   // 鏂版坊鍔犳爣蹇楋紝鐢ㄤ簬鍒濆鍖�
+      }
+      
+      let targetId = ''
+
+      if (item.dropTargetId) {
+        targetId = item.dropTargetId
+        delete item.dropTargetId
+      } else if (cards.length > 0) {
+        targetId = cards.slice(-1)[0].uuid
+      }
+
+      const { index: overIndex } = findCard(`${targetId}`)
+      const _cards = update(cards, { $splice: [[overIndex + 1, 0, newcard]] })
+
+      handleList({...config, components: _cards})
+    }
+  })
+
+  return (
+    <div ref={drop} className="ant-row tab-shell-inner">
+      {cards.map(card => (
+        <Card
+          id={card.uuid}
+          key={card.uuid}
+          config={config}
+          card={card}
+          moveCard={moveCard}
+          delCard={deleteCard}
+          findCard={findCard}
+          updateConfig={updateConfig}
+        />
+      ))}
+      {cards.length === 0 ?
+        <Empty description="璇锋坊鍔犵粍浠�" /> : null
+      }
+    </div>
+  )
+}
+export default Container
diff --git a/src/mob/components/tabs/tabcomponents/index.scss b/src/mob/components/tabs/tabcomponents/index.scss
new file mode 100644
index 0000000..f21f784
--- /dev/null
+++ b/src/mob/components/tabs/tabcomponents/index.scss
@@ -0,0 +1,14 @@
+.tab-shell-inner {
+  margin: 0px;
+
+  .anticon {
+    cursor: unset;
+  }
+
+  .mk-component-card {
+    position: relative;
+  }
+  >.ant-empty {
+    padding: 60px 0px 70px;
+  }
+}
\ No newline at end of file
diff --git a/src/mob/components/topbar/normal-navbar/index.jsx b/src/mob/components/topbar/normal-navbar/index.jsx
index db75aed..59a358e 100644
--- a/src/mob/components/topbar/normal-navbar/index.jsx
+++ b/src/mob/components/topbar/normal-navbar/index.jsx
@@ -158,13 +158,8 @@
   render() {
     const { card } = this.state
 
-    let _style = {...card.style}
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
-    }
-
     return (
-      <div className="normal-topbar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
+      <div className="normal-topbar-edit-box" style={card.style} onClick={this.clickComponent} id={card.uuid}>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <WrapComponent config={card} updateConfig={this.updateComponent} />
diff --git a/src/mob/mobshell/card.jsx b/src/mob/mobshell/card.jsx
index 9cd7402..797d51a 100644
--- a/src/mob/mobshell/card.jsx
+++ b/src/mob/mobshell/card.jsx
@@ -9,7 +9,7 @@
 const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
 const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
 const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
-const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
+const AntvTabs = asyncComponent(() => import('@/mob/components/tabs/antv-tabs'))
 const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
 const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
 const CarouselDataCard = asyncComponent(() => import('@/menu/components/carousel/data-card'))
diff --git a/src/mob/mobshell/index.jsx b/src/mob/mobshell/index.jsx
index 5cdb407..6d14bee 100644
--- a/src/mob/mobshell/index.jsx
+++ b/src/mob/mobshell/index.jsx
@@ -122,6 +122,7 @@
         card: '鍗$墖',
         navbar: '瀵艰埅鏍�',
         menubar: '鑿滃崟鏍�',
+        balcony: '娴姩鍗�',
         login: '鐧诲綍'
       }
       let i = 1
diff --git a/src/pc/components/navbar/normal-navbar/index.jsx b/src/pc/components/navbar/normal-navbar/index.jsx
index b70d77b..11ce5dd 100644
--- a/src/pc/components/navbar/normal-navbar/index.jsx
+++ b/src/pc/components/navbar/normal-navbar/index.jsx
@@ -166,13 +166,8 @@
   render() {
     const { card } = this.state
 
-    let _style = {...card.style}
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
-    }
-
     return (
-      <div className="normal-navbar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
+      <div className="normal-navbar-edit-box" style={card.style} onClick={this.clickComponent} id={card.uuid}>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <MenuComponent config={card} updateConfig={this.updateComponent} />
diff --git a/src/pc/menushell/index.jsx b/src/pc/menushell/index.jsx
index 24e3910..2ad5bd7 100644
--- a/src/pc/menushell/index.jsx
+++ b/src/pc/menushell/index.jsx
@@ -112,6 +112,7 @@
         dashboard: '浠〃鐩�',
         tree: '鏍戝舰鍒楄〃',
         card: '鍗$墖',
+        balcony: '娴姩鍗�',
         login: '鐧诲綍'
       }
       let i = 1
diff --git a/src/tabviews/rolemanage/index.jsx b/src/tabviews/rolemanage/index.jsx
index 99b8022..f334a5c 100644
--- a/src/tabviews/rolemanage/index.jsx
+++ b/src/tabviews/rolemanage/index.jsx
@@ -1,7 +1,8 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Card, Col, Row, Icon, Menu, notification, Spin, Input, Tabs, Button, Tree, Empty } from 'antd'
+import { Card, Col, Row, Icon, Menu, notification, Spin, Input, Tabs, Button, Tree, Empty, Select } from 'antd'
+import md5 from 'md5'
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
@@ -20,14 +21,16 @@
 export default class RoleManage extends Component {
   static propTpyes = {
     MenuNo: PropTypes.string, // 鑿滃崟鍙傛暟
-    MenuID: PropTypes.string // 鑿滃崟Id
+    MenuID: PropTypes.string  // 鑿滃崟Id
   }
 
   state = {
     dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     loading: true,
     loadingTree: false,
+    loadingAppTree: false,
     roleList: null,
+    filterRoleList: [],
     selectRoleId: '',
     mainMenus: null,
     menuTrees: null,
@@ -36,9 +39,17 @@
     selectMenuTrees: null,
     selectMenuOpenKeys: [],
     primarykey: '',
-    tabKey: '',
     submitloading: false,
-    initCheckKeys: null
+    initCheckKeys: null,
+    activeKey: 'manage',
+    appTrees: null,
+    appCheckedKeys: [],
+    appOpenKeys: [],
+    applist: [],
+    selectApp: null,
+    selectSubApp: null,
+    selectAppTrees: null,
+    selectAppOpenKeys: [],
   }
 
   /**
@@ -52,7 +63,8 @@
 
     if (result.status) {
       this.setState({
-        roleList: result.data
+        roleList: result.data,
+        filterRoleList: result.data || []
       })
     } else {
       notification.warning({
@@ -61,6 +73,35 @@
         duration: 5
       })
     }
+  }
+
+  getAppList = () => {
+    let param = {
+      func: 's_get_kei'
+    }
+
+    Api.getCloudConfig(param).then(result => {
+      if (result.status) {
+        let applist = result.data.map(item => {
+          item.sublist = item.data_detail || []
+          item.sublist = item.sublist.map(cell => {
+            cell.ID = cell.d_id
+            return cell
+          })
+
+          return item
+        })
+        let selectApp = applist[0] || null
+
+        this.setState({ applist, selectApp })
+      } else {
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+      }
+    })
   }
 
   /**
@@ -103,12 +144,12 @@
    * @description 鑾峰彇鎵�鏈夎彍鍗曡妭鐐癸紝褰㈡垚鏉冮檺鏍�
    */
   getAllMenuList = async () => {
-    const { selectRoleId, mainMenus, tabKey } = this.state
+    const { selectRoleId, mainMenus } = this.state
 
     let param = {
       func: 's_rolemenu_get_FunMenu',
       RoleID: selectRoleId,
-      SelectedType: tabKey
+      SelectedType: ''
     }
 
     let result = await Api.getSystemConfig(param)
@@ -153,61 +194,147 @@
    * @description 閫夋嫨瑙掕壊涓斿瓨鍦ㄦ潈闄愭爲鏃讹紝鑾峰彇宸插垎閰嶇粨鏋�
    */
   getSelectMenuList = async () => {
-    const { selectRoleId, menuTrees, tabKey } = this.state
-    if (!menuTrees) return
+    const { selectRoleId, menuTrees, activeKey } = this.state
+    if (!menuTrees || !selectRoleId || activeKey !== 'manage') return
 
     let param = {
       func: 's_rolemenu_get_Menulist',
       RoleID: selectRoleId
     }
 
+    this.setState({
+      loadingTree: true
+    })
+
     let result = await Api.getSystemConfig(param)
 
     if (result.status) {
       let _openKeys = []
 
-      if (tabKey === '') {
-        let _initKeys = result.data.map(item => item.MenuID)
-        _initKeys = Array.from(new Set(_initKeys))
+      let _initKeys = result.data.map(item => item.MenuID)
+      _initKeys = Array.from(new Set(_initKeys))
 
-        selectMap = new Map()
-        this.getCheckedKeys(fromJS(menuTrees).toJS(), _initKeys)
+      selectMap = new Map()
+      this.getCheckedKeys(fromJS(menuTrees).toJS(), _initKeys)
 
-        if (menuTrees[0]) {
-          if (menuTrees[0].key === 'PC' && menuTrees[0].children) {
-            this.getOpenNode(menuTrees[0].children[0], _openKeys)
-          } else {
-            this.getOpenNode(menuTrees[0], _openKeys)
-          }
+      if (menuTrees[0]) {
+        if (menuTrees[0].key === 'PC' && menuTrees[0].children) {
+          this.getOpenNode(menuTrees[0].children[0], _openKeys)
+        } else {
+          this.getOpenNode(menuTrees[0], _openKeys)
         }
-
-        this.setState({
-          loadingTree: false,
-          initCheckKeys: _initKeys,
-          checkedKeys: Array.from(selectMap.keys()),
-          menuOpenKeys: _openKeys
-        })
-      } else {
-        let Keys = result.data.map(item => item.MenuID)
-        let _tree = this.getSelectTree(fromJS(menuTrees).toJS(), Keys)
-
-        if (_tree[0]) {
-          if (_tree[0].key === 'PC' && _tree[0].children) {
-            this.getOpenNode(_tree[0].children[0], _openKeys)
-          } else {
-            this.getOpenNode(_tree[0], _openKeys)
-          }
-        }
-
-        this.setState({
-          loadingTree: false,
-          selectMenuTrees: _tree,
-          selectMenuOpenKeys: _openKeys
-        })
       }
+
+      let _openkeys = []
+      let Keys = result.data.map(item => item.MenuID)
+      let _tree = this.getSelectTree(fromJS(menuTrees).toJS(), Keys)
+
+      if (_tree[0]) {
+        if (_tree[0].key === 'PC' && _tree[0].children) {
+          this.getOpenNode(_tree[0].children[0], _openkeys)
+        } else {
+          this.getOpenNode(_tree[0], _openkeys)
+        }
+      }
+
+      this.setState({
+        loadingTree: false,
+        initCheckKeys: _initKeys,
+        checkedKeys: Array.from(selectMap.keys()),
+        menuOpenKeys: _openKeys,
+        selectMenuTrees: _tree,
+        selectMenuOpenKeys: _openkeys
+      })
     } else {
       this.setState({
         loadingTree: false
+      })
+      notification.warning({
+        top: 92,
+        message: result.message,
+        duration: 5
+      })
+    }
+  }
+
+  /**
+   * @description 閫夋嫨瑙掕壊涓斿瓨鍦ㄦ潈闄愭爲鏃讹紝鑾峰彇宸插垎閰嶇粨鏋�
+   */
+  getSelectAppNodeList = async () => {
+    const { selectRoleId, selectSubApp, selectApp, appTrees, activeKey } = this.state
+
+    if (!appTrees || !selectRoleId || activeKey !== 'app' || !selectSubApp || !selectApp) return
+
+    let param = {
+      func: 's_rolemenu_get_Menulist',
+      RoleID: selectRoleId,
+      TypeCharOne: selectApp.kei_no,
+      typename: selectSubApp.typename,
+      lang: selectSubApp.lang
+    }
+
+    this.setState({
+      loadingAppTree: true
+    })
+
+    let result = await Api.getSystemConfig(param)
+
+    if (result.status) {
+      let _initKeys = result.data.map(item => item.MenuID)
+      _initKeys = Array.from(new Set(_initKeys))
+      let _checkedKeys = []
+
+      let getCheckedKeys = (parents) => {
+        parents.forEach(item => {
+          if (_initKeys.includes(item.key)) {
+            if (item.children && item.children.length > 0) {
+              getCheckedKeys(item.children)
+            } else {
+              _checkedKeys.push(item.key)
+            }
+          }
+        })
+      }
+
+      getCheckedKeys(appTrees)
+
+      let _openKeys = []
+      let getOpenNode = (parentNode) => {
+        if (parentNode && parentNode.children && parentNode.children.length > 0) {
+          _openKeys.push(parentNode.key)
+          parentNode.children.forEach(node => {
+            getOpenNode(node)
+          })
+        }
+      }
+      getOpenNode(appTrees[0])
+
+      let Keys = result.data.map(item => item.MenuID)
+      let _tree = this.getSelectTree(fromJS(appTrees).toJS(), Keys)
+
+
+      let _openkeys = []
+      let _getOpenNode = (parentNode) => {
+        if (parentNode && parentNode.children && parentNode.children.length > 0) {
+          _openkeys.push(parentNode.key)
+          parentNode.children.forEach(node => {
+            _getOpenNode(node)
+          })
+        }
+      }
+      _getOpenNode(_tree[0])
+
+      this.setState({
+        loadingAppTree: false,
+        appInitCheckKeys: _initKeys,
+        appCheckedKeys: _checkedKeys,
+        appOpenKeys: _openKeys,
+        selectAppTrees: _tree,
+        selectAppOpenKeys: _openkeys
+      })
+    } else {
+      this.setState({
+        loadingAppTree: false
       })
       notification.warning({
         top: 92,
@@ -334,10 +461,10 @@
     if (selectRoleId === role.RoleID) return
 
     this.setState({
-      selectRoleId: role.RoleID,
-      loadingTree: true
+      selectRoleId: role.RoleID
     }, () => {
       this.getSelectMenuList()
+      this.getSelectAppNodeList()
     })
   }
 
@@ -369,23 +496,13 @@
   }
 
   /**
-   * @description 宸插垎閰嶄笌鏈垎閰嶅垏鎹�
+   * @description 鑺傜偣閫夋嫨浜嬩欢
    */
-  changeTab = (key) => {
-    const { selectRoleId } = this.state
-
+  onAppCheck = (checkedKeys, info) => {
     this.setState({
-      tabKey: key === 'selected' ? key : '',
-      loadingTree: true
-    }, () => {
-      if (selectRoleId) {
-        this.getSelectMenuList()
-      } else {
-        this.setState({
-          selectMenuTrees: [],
-          loadingTree: false
-        })
-      }
+      appCheckedKeys: checkedKeys,
+      appHalfCheckedKeys: info.halfCheckedKeys,
+      appInitCheckKeys: null
     })
   }
 
@@ -429,36 +546,88 @@
 
     Api.getSystemConfig(param).then(result => {
       if (result.status) {
-        if (!window.GLOB.mainSystemApi) {
-          notification.success({
-            top: 92,
-            message: '淇濆瓨鎴愬姛',
-            duration: 2
-          })
-          this.setState({
-            submitloading: false,
-            loadingTree: true
-          }, () => {
-            this.getSelectMenuList()
-          })
-        } else {
+        notification.success({
+          top: 92,
+          message: '淇濆瓨鎴愬姛',
+          duration: 2
+        })
+        this.setState({
+          submitloading: false
+        }, () => {
+          this.getSelectMenuList()
+        })
+        if (window.GLOB.mainSystemApi) {
           Api.getLocalConfig(localParam).then(res => {
-            if (res.status) {
-              notification.success({
+            if (!res.status) {
+              notification.warning({
                 top: 92,
-                message: '淇濆瓨鎴愬姛',
-                duration: 2
+                message: res.message,
+                duration: 5
               })
-              this.setState({
-                submitloading: false,
-                loadingTree: true
-              }, () => {
-                this.getSelectMenuList()
-              })
-            } else {
-              this.setState({
-                submitloading: false
-              })
+            }
+          })
+        }
+      } else {
+        this.setState({
+          submitloading: false
+        })
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  /**
+   * @description 鎻愪氦宸查�夌殑鏉冮檺
+   */
+  appRoleSubmit = () => {
+    const { selectApp, selectSubApp, appCheckedKeys, appHalfCheckedKeys, selectRoleId, appInitCheckKeys } = this.state
+
+    let _keys = []
+
+    if (appInitCheckKeys) {
+      _keys = appInitCheckKeys
+    } else {
+      _keys = appCheckedKeys.concat(appHalfCheckedKeys)
+    }
+
+    let param = {
+      func: 's_rolemenu_sub',
+      RoleID: selectRoleId,
+      TypeCharOne: selectApp.kei_no,
+      typename: selectSubApp.typename,
+      lang: selectSubApp.lang,
+      RoleMenu: _keys.map(key => {
+        return {MenuID: key}
+      })
+    }
+
+    let localParam = fromJS(param).toJS()
+    localParam.func = 's_rolemenu_sub_local'
+
+    this.setState({
+      submitloading: true
+    })
+
+    Api.getSystemConfig(param).then(result => {
+      if (result.status) {
+        notification.success({
+          top: 92,
+          message: '淇濆瓨鎴愬姛',
+          duration: 2
+        })
+        this.setState({
+          submitloading: false
+        }, () => {
+          this.getSelectAppNodeList()
+        })
+        
+        if (window.GLOB.mainSystemApi) {
+          Api.getLocalConfig(localParam).then(res => {
+            if (!res.status) {
               notification.warning({
                 top: 92,
                 message: res.message,
@@ -489,25 +658,168 @@
       loading: true,
       loadingTree: false,
       roleList: null,
+      filterRoleList: [],
       selectRoleId: '',
       mainMenus: null,
       menuTrees: null,
       checkedKeys: [],
+      appCheckedKeys: [],
       menuOpenKeys: [],
       selectMenuTrees: null,
       selectMenuOpenKeys: [],
       primarykey: '',
-      tabKey: '',
       submitloading: false,
-      initCheckKeys: null
+      initCheckKeys: null,
+      appInitCheckKeys: null,
+      activeKey: 'manage',
+      appTrees: null,
+      appOpenKeys: [],
+      applist: [],
+      selectApp: null,
+      selectSubApp: null,
+      selectAppTrees: null,
+      selectAppOpenKeys: []
     })
     this.getRoleList()
     this.getMainMenuList()
+    this.getAppList()
+  }
+
+  filterRole = () => {
+    const { primarykey, roleList } = this.state
+
+    let _roleList  = []
+
+    if (roleList && roleList.length > 0) {
+      _roleList = roleList.filter(role => role.RoleName.toLowerCase().indexOf(primarykey.toLowerCase()) >= 0)
+    }
+
+    this.setState({filterRoleList: _roleList})
+  }
+
+  getTreeList = () => {
+    const { selectApp, selectSubApp } = this.state
+    let param = {
+      func: 's_get_menus_roles_tree',
+      typecharone: selectApp.kei_no,
+      lang: selectSubApp.lang
+    }
+
+    param.upid = md5(window.GLOB.appkey + selectApp.kei_no + selectSubApp.typename + selectSubApp.lang)
+
+    this.setState({loadingAppTree: true})
+
+    Api.getCloudConfig(param).then(result => {
+      if (result.status) {
+        if (!result.data || result.data.length === 0) {
+          this.setState({loadingAppTree: false, appTrees: []})
+        } else {
+          this.initTrees(result.data)
+        }
+      } else {
+        this.setState({loadingAppTree: false, appTrees: []})
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  initTrees = (data) => {
+    let trees = []
+    let map = new Map()
+    let _data = data.sort((a, b) => {
+      return a.sort - b.sort
+    })
+
+    _data.forEach(menu => {
+      if (menu.ParentID === 'top') {
+        trees.push({
+          key: menu.MenuID,
+          title: menu.MenuName,
+          children: []
+        })
+      } else {
+        map.set(menu.MenuID, menu)
+      }
+    })
+
+    let reset = (m) => {
+      return m.map(n => {
+        [...map.keys()].forEach(key => {
+          if (map.get(key).ParentID === n.key) {
+            let c = map.get(key)
+            n.children.push({
+              key: c.MenuID,
+              title: c.MenuName,
+              children: []
+            })
+            map.delete(key)
+          }
+        })
+        if (n.children.length > 0) {
+          n.children = reset(n.children)
+        }
+        return n
+      })
+    }
+
+    trees = reset(trees)
+
+    let expandedKeys = this.getExpandedKeys(trees, 0, [])
+
+    this.setState({loadingAppTree: false, appTrees: trees, appOpenKeys: expandedKeys}, () => {
+      this.getSelectAppNodeList()
+    })
+  }
+
+  getExpandedKeys = (trees, i, keys) => {
+    if (i >= 3 || !trees[0]) return keys
+
+    keys.push(trees[0].key)
+
+    i++
+
+    if (trees[0].children && trees[0].children.length > 0) {
+      keys = this.getExpandedKeys(trees[0].children, i, keys)
+    }
+
+    return keys
+  }
+
+  changeType = (val) => {
+    this.setState({
+      activeKey: val
+    }, () => {
+      this.getSelectMenuList()
+      this.getSelectAppNodeList()
+    })
+  }
+
+  changeApp = (val) => {
+    const { applist } = this.state
+
+    let app = applist.filter(item => item.ID === val)[0]
+
+    this.setState({selectApp: app, selectSubApp: null})
+  }
+  
+  changeSubApp = (val) => {
+    const { selectApp } = this.state
+
+    let subapp = selectApp.sublist.filter(item => item.ID === val)[0]
+
+    this.setState({selectSubApp: subapp}, () => {
+      this.getTreeList()
+    })
   }
 
   UNSAFE_componentWillMount () {
     this.getRoleList()
     this.getMainMenuList()
+    this.getAppList()
   }
 
   componentDidMount () {
@@ -529,66 +841,134 @@
   }
 
   render() {
-    const { roleList, loading, loadingTree, primarykey, menuTrees, checkedKeys, menuOpenKeys, selectMenuTrees, tabKey, selectRoleId, selectMenuOpenKeys, submitloading } = this.state
-
-    let _roleList  = []
-
-    if (roleList && roleList.length > 0) {
-      _roleList = roleList.filter(role => role.RoleName.toLowerCase().indexOf(primarykey.toLowerCase()) >= 0)
-    }
+    const { activeKey, filterRoleList, applist, selectApp, selectSubApp, loading, loadingTree, loadingAppTree, primarykey, menuTrees, appTrees, checkedKeys, appCheckedKeys, menuOpenKeys, selectMenuTrees, selectAppTrees, selectRoleId, selectMenuOpenKeys, selectAppOpenKeys, submitloading, appOpenKeys } = this.state
 
     return (
       <div className="rolemanage">
         {loading && <Spin size="large" />}
-        <Row gutter={16}>
-          <Col span={5}>
-            <Card
-              className="role-list"
-              title={
-                <span className="role-title">
-                  <Icon type="bank" />
-                  <span className="title">{this.state.dict['main.role.title']}</span>
-                  <Search placeholder="" onSearch={value => this.setState({primarykey: value})} />
-                </span>
-              }
-              bordered={false}
-            >
-              <Menu
-                onClick={this.handleClick}
-                mode="inline"
-              >
-                {_roleList.map((role, index) =>
-                  <Menu.Item key={index} onClick={() => this.changeRole(role)}>{role.RoleName}</Menu.Item>
-                )}
-              </Menu>
-            </Card>
-          </Col>
-          <Col span={19}>
-            <Tabs defaultActiveKey="all" tabBarExtraContent={!tabKey && selectRoleId ? <Button type="primary" loading={submitloading} onClick={this.roleSubmit}>鎻愪氦</Button> : null} onChange={this.changeTab}>
-              <TabPane tab="鑿滃崟鍒楄〃" key="all">
-                {!loadingTree && menuTrees && menuTrees.length > 0 ? <Tree
-                  checkable
-                  selectable={false}
-                  defaultExpandedKeys={menuOpenKeys}
-                  autoExpandParent={true}
-                  onCheck={this.onCheck}
-                  checkedKeys={checkedKeys}
+        <Tabs activeKey={activeKey} type="card" onChange={this.changeType}>
+          <TabPane tab="绠$悊绯荤粺" key="manage">
+            <Row gutter={16}>
+              <Col span={5}>
+                <Card
+                  className="role-list"
+                  title={
+                    <span className="role-title">
+                      <Icon type="bank" />
+                      <span className="title">瑙掕壊</span>
+                      <Search placeholder="" value={primarykey} onChange={e => this.setState({primarykey: e.target.value})} onSearch={this.filterRole} />
+                    </span>
+                  }
+                  bordered={false}
                 >
-                  {this.renderTreeNodes(menuTrees)}
-                </Tree> : null}
-                {!loadingTree && (!menuTrees || menuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
-                {loadingTree ? <Spin className="load-tree" /> : null}
-              </TabPane>
-              <TabPane tab="宸叉巿鏉冭彍鍗�" key="selected">
-                {!loadingTree && selectMenuTrees && selectMenuTrees.length > 0 ? <DirectoryTree multiple defaultExpandedKeys={selectMenuOpenKeys}>
-                  {this.renderTreeNodes(selectMenuTrees)}
-                </DirectoryTree> : null}
-                {!loadingTree && (!selectMenuTrees || selectMenuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
-                {loadingTree ? <Spin className="load-tree" /> : null}
-              </TabPane>
-            </Tabs>
-          </Col>
-        </Row>
+                  <Menu selectedKeys={[selectRoleId]} mode="inline">
+                    {filterRoleList.map((role) =>
+                      <Menu.Item key={role.RoleID} onClick={() => this.changeRole(role)}>{role.RoleName}</Menu.Item>
+                    )}
+                  </Menu>
+                </Card>
+              </Col>
+              <Col span={19}>
+                <Tabs defaultActiveKey="all">
+                  <TabPane tab="鏉冮檺" key="all">
+                    {selectRoleId ? <Button className="submitBtn" type="primary" loading={submitloading} onClick={this.roleSubmit}>鎻愪氦</Button> : null}
+                    {!loadingTree && menuTrees && menuTrees.length > 0 ? <Tree
+                      checkable
+                      selectable={false}
+                      defaultExpandedKeys={menuOpenKeys}
+                      autoExpandParent={true}
+                      onCheck={this.onCheck}
+                      checkedKeys={checkedKeys}
+                    >
+                      {this.renderTreeNodes(menuTrees)}
+                    </Tree> : null}
+                    {!loadingTree && (!menuTrees || menuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
+                    {loadingTree ? <Spin className="load-tree" /> : null}
+                  </TabPane>
+                  <TabPane tab="宸叉巿鏉�" key="selected">
+                    {!loadingTree && selectMenuTrees && selectMenuTrees.length > 0 ? <DirectoryTree multiple defaultExpandedKeys={selectMenuOpenKeys}>
+                      {this.renderTreeNodes(selectMenuTrees)}
+                    </DirectoryTree> : null}
+                    {!loadingTree && (!selectMenuTrees || selectMenuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
+                    {loadingTree ? <Spin className="load-tree" /> : null}
+                  </TabPane>
+                </Tabs>
+              </Col>
+            </Row>
+          </TabPane>
+          <TabPane tab="搴旂敤" key="app">
+            <div className="app-select">
+              <div className="mk-form-item">
+                <label>搴旂敤:</label>
+                <Select value={selectApp ? selectApp.ID : ''} onChange={this.changeApp}>
+                  {applist.map(option =>
+                    <Select.Option key={option.ID} value={option.ID}>{option.remark}</Select.Option>
+                  )}
+                </Select>
+              </div>
+              <div className="mk-form-item">
+                <label>瀛愬簲鐢�:</label>
+                <Select value={selectSubApp ? selectSubApp.ID : ''} onChange={this.changeSubApp}>
+                  {selectApp && selectApp.sublist.map(option =>
+                    <Select.Option key={option.ID} value={option.ID}>{`${option.typename}锛�${option.lang}锛塦}</Select.Option>
+                  )}
+                </Select>
+              </div>
+            </div>
+            <Row gutter={16}>
+              <Col span={5}>
+                <Card
+                  className="role-list"
+                  title={
+                    <span className="role-title">
+                      <Icon type="bank" />
+                      <span className="title">瑙掕壊</span>
+                      <Search placeholder="" value={primarykey} onChange={e => this.setState({primarykey: e.target.value})} onSearch={this.filterRole} />
+                    </span>
+                  }
+                  bordered={false}
+                >
+                  <Menu selectedKeys={[selectRoleId]} mode="inline">
+                    {filterRoleList.map((role) =>
+                      <Menu.Item key={role.RoleID} onClick={() => this.changeRole(role)}>{role.RoleName}</Menu.Item>
+                    )}
+                  </Menu>
+                </Card>
+              </Col>
+              <Col span={19}>
+                <Tabs defaultActiveKey="all">
+                  <TabPane tab="鏉冮檺" key="all">
+                    {selectSubApp && selectRoleId ? <Button className="submitBtn" type="primary" loading={submitloading} onClick={this.appRoleSubmit}>鎻愪氦</Button> : null}
+                    {selectSubApp ? <div>
+                      {!loadingAppTree && appTrees && appTrees.length > 0 ? <Tree
+                        checkable
+                        selectable={false}
+                        defaultExpandedKeys={appOpenKeys}
+                        autoExpandParent={true}
+                        onCheck={this.onAppCheck}
+                        checkedKeys={appCheckedKeys}
+                      >
+                        {this.renderTreeNodes(appTrees)}
+                      </Tree> : null}
+                      {!loadingAppTree && (!appTrees || appTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
+                      {loadingAppTree ? <Spin className="load-tree" /> : null}
+                    </div> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={'璇烽�夋嫨瀛愬簲鐢�'} />}
+                  </TabPane>
+                  <TabPane tab="宸叉巿鏉�" key="selected">
+                    {selectSubApp ? <div>
+                      {!loadingAppTree && selectAppTrees && selectAppTrees.length > 0 ? <DirectoryTree multiple defaultExpandedKeys={selectAppOpenKeys}>
+                        {this.renderTreeNodes(selectAppTrees)}
+                      </DirectoryTree> : null}
+                      {!loadingAppTree && (!selectAppTrees || selectAppTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
+                      {loadingAppTree ? <Spin className="load-tree" /> : null}
+                    </div> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={'璇烽�夋嫨瀛愬簲鐢�'} />}
+                  </TabPane>
+                </Tabs>
+              </Col>
+            </Row>
+          </TabPane>
+        </Tabs>
+        
       </div>
     )
   }
diff --git a/src/tabviews/rolemanage/index.scss b/src/tabviews/rolemanage/index.scss
index 7140d77..80704e7 100644
--- a/src/tabviews/rolemanage/index.scss
+++ b/src/tabviews/rolemanage/index.scss
@@ -14,7 +14,7 @@
         .role-title {
           display: inline-block;
           width: 100%;
-          color: #1890ff;
+          color: rgba(0, 0, 0, 0.65);
           .anticon-bank {
             margin-right: 5px;
           }
@@ -45,8 +45,14 @@
     left: calc(50vw - 22px);
     top: calc(50vh - 70px);
     z-index: 1;
-  } 
-  .ant-tabs {
+  }
+  .ant-tabs-bar.ant-tabs-card-bar {
+    .ant-tabs-tab {
+      min-width: 120px;
+      text-align: center;
+    }
+  }
+  .ant-tabs.ant-tabs-line {
     background: #fff;
     min-height: calc(100vh - 125px);
     box-shadow: 0px 0px 2px #eeeeee;
@@ -74,6 +80,30 @@
         text-align: center;
         color: #bcbcbc;
       }
+      .submitBtn {
+        position: absolute;
+        right: 10px;
+        top: 15px;
+      }
+    }
+  }
+  .app-select {
+    position: absolute;
+    top: 0px;
+    right: 0px;
+    .mk-form-item {
+      display: inline-block;
+      width: 230px;
+      label {
+        width: 60px;
+        display: inline-block;
+        text-align: right;
+        padding-right: 5px;
+        white-space: nowrap;
+      }
+      .ant-select {
+        width: 150px;
+      }
     }
   }
 }
\ No newline at end of file

--
Gitblit v1.8.0