From 6c56a138c17dc9aff9d175d20645eb176d2e024b Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期三, 03 三月 2021 19:03:18 +0800
Subject: [PATCH] 2021-03-03

---
 src/pc/components/navbar/normal-navbar/index.jsx                                 |  322 +------------
 src/pc/components/navbar/normal-navbar/wrapsetting/index.jsx                     |   83 +++
 src/pc/modulesource/option.jsx                                                   |    2 
 src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx |   10 
 src/pc/components/navbar/normal-navbar/menusetting/menutable/index.scss          |   10 
 src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss        |   11 
 src/pc/menushell/index.jsx                                                       |  165 +++++++
 src/pc/components/navbar/normal-navbar/menusetting/index.jsx                     |   73 +++
 src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx           |  168 +++++++
 src/pc/menushell/index.scss                                                      |   23 +
 src/pc/components/navbar/normal-navbar/menusetting/menuform/index.scss           |    0 
 src/pc/components/navbar/normal-navbar/wrapsetting/index.scss                    |    7 
 src/index.js                                                                     |    2 
 src/assets/mobimg/navbar.png                                                     |    0 
 src/menu/stylecontroller/index.jsx                                               |   20 
 src/views/pcdesign/index.jsx                                                     |    5 
 src/pc/menushell/card.jsx                                                        |   90 +++
 src/menu/components/tabs/tablabelform/index.jsx                                  |   31 +
 src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx            |  133 +++++
 src/menu/components/share/actioncomponent/actionform/index.jsx                   |    5 
 src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx         |  137 +++++
 src/tabviews/custom/index.jsx                                                    |   10 
 src/pc/components/navbar/normal-navbar/index.scss                                |   39 +
 src/pc/components/navbar/normal-navbar/menusetting/index.scss                    |    7 
 package.json                                                                     |    2 
 src/menu/components/tabs/antv-tabs/index.jsx                                     |    1 
 26 files changed, 1,055 insertions(+), 301 deletions(-)

diff --git a/package.json b/package.json
index ad7bc05..dc3fd68 100644
--- a/package.json
+++ b/package.json
@@ -188,7 +188,7 @@
       ]
     ]
   },
-  "homepage": "./build",
+  "homepage": ".",
   "devDependencies": {
     "typescript": "^4.0.2"
   }
diff --git a/src/assets/mobimg/navbar.png b/src/assets/mobimg/navbar.png
new file mode 100644
index 0000000..1ea001e
--- /dev/null
+++ b/src/assets/mobimg/navbar.png
Binary files differ
diff --git a/src/index.js b/src/index.js
index 4f87b72..f53f790 100644
--- a/src/index.js
+++ b/src/index.js
@@ -60,7 +60,7 @@
 // 娴嬭瘯绯荤粺鏂囦欢缃簬admin涓�
 
 // fetch(process.env.NODE_ENV === 'production' ? '../options.json' : './options.json')
-fetch('./options.json')
+fetch('../options.json')
   .then(response => response.json())
   .catch(() => {
     document.getElementById('root').innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh;">绯荤粺閰嶇疆淇℃伅鑾峰彇澶辫触锛岃鑱旂郴绠$悊鍛橈紒</div>'
diff --git a/src/menu/components/share/actioncomponent/actionform/index.jsx b/src/menu/components/share/actioncomponent/actionform/index.jsx
index db302a9..bd7648b 100644
--- a/src/menu/components/share/actioncomponent/actionform/index.jsx
+++ b/src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -266,7 +266,7 @@
         this.props.form.setFieldsValue(_fieldval)
       })
     } else if (key === 'funcType') {
-      let _options = this.getOptions(this.state.openType, this.state.interType, value, card.pageTemplate, procMode)
+      let _options = this.getOptions(openType, this.state.interType, value, card.pageTemplate, procMode)
       let _fieldval = {}
 
       this.setState({
@@ -330,10 +330,9 @@
       })
     } else if (key === 'pageTemplate') {
       let _fieldval = {}
-      let _options = this.getOptions(this.state.openType, this.state.interType, this.state.funcType, value, procMode)
+      let _options = this.getOptions(openType, this.state.interType, this.state.funcType, value, procMode)
 
       this.setState({
-        openType: value,
         formlist: this.state.formlist.map(item => {
           item.hidden = !_options.includes(item.key)
 
diff --git a/src/menu/components/tabs/antv-tabs/index.jsx b/src/menu/components/tabs/antv-tabs/index.jsx
index 2a6e059..01c055b 100644
--- a/src/menu/components/tabs/antv-tabs/index.jsx
+++ b/src/menu/components/tabs/antv-tabs/index.jsx
@@ -216,6 +216,7 @@
     this.tabLabelRef.handleConfirm().then(res => {
       editab.label = res.label
       editab.icon = res.icon
+      editab.blacklist = res.blacklist
 
       if (editab.uuid) {
         tabs.subtabs = tabs.subtabs.map(t => {
diff --git a/src/menu/components/tabs/tablabelform/index.jsx b/src/menu/components/tabs/tablabelform/index.jsx
index 3a63a0d..784a9f9 100644
--- a/src/menu/components/tabs/tablabelform/index.jsx
+++ b/src/menu/components/tabs/tablabelform/index.jsx
@@ -12,10 +12,21 @@
     inputSubmit: PropTypes.func    // 鍥炶溅浜嬩欢
   }
 
-  state = {}
+  state = {roleList: []}
 
   UNSAFE_componentWillMount () {
+    let roleList = sessionStorage.getItem('sysRoles')
+    if (roleList) {
+      try {
+        roleList = JSON.parse(roleList)
+      } catch {
+        roleList = []
+      }
+    } else {
+      roleList = []
+    }
 
+    this.setState({roleList})
   }
 
   handleConfirm = () => {
@@ -42,6 +53,7 @@
   render() {
     const { tab } = this.props
     const { getFieldDecorator } = this.props.form
+    const { roleList } = this.state
 
     const formItemLayout = {
       labelCol: {
@@ -85,6 +97,23 @@
               )}
             </Form.Item>
           </Col>
+          <Col span={24}>
+            <Form.Item label="榛戝悕鍗�">
+              {getFieldDecorator('blacklist', {
+                initialValue: tab.blacklist || []
+              })(
+                <Select
+                  showSearch
+                  mode="multiple"
+                  filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                >
+                  {roleList.map(option =>
+                    <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
         </Row>
       </Form>
     )
diff --git a/src/menu/stylecontroller/index.jsx b/src/menu/stylecontroller/index.jsx
index 9735a7e..cc674a1 100644
--- a/src/menu/stylecontroller/index.jsx
+++ b/src/menu/stylecontroller/index.jsx
@@ -268,6 +268,15 @@
     this.updateStyle(_style)
   }
 
+  changeWidth = (val) => {
+    let _val = val
+    if (_val === '0px') {
+      _val = 'auto'
+    }
+
+    this.updateStyle({width: _val})
+  }
+
   changeHeight = (val) => {
     let _val = val
     if (_val === '0px') {
@@ -313,6 +322,17 @@
         <div className="menu-style-controller">
           <Form {...formItemLayout}>
             {card ? <Collapse expandIconPosition="right" destroyInactivePanel={true} defaultActiveKey={options[0]}>
+              {options.includes('width') ? <Panel header="瀹藉害" key="width">
+                <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.width || ''} options={['px']} onChange={this.changeWidth}/>
+                  </Form.Item>
+                </Col>
+              </Panel> : null}
               {options.includes('height') ? <Panel header="楂樺害" key="height">
                 <Col span={24}>
                   <Form.Item
diff --git a/src/pc/components/navbar/normal-navbar/index.jsx b/src/pc/components/navbar/normal-navbar/index.jsx
index 1ce4fff..a67fe8e 100644
--- a/src/pc/components/navbar/normal-navbar/index.jsx
+++ b/src/pc/components/navbar/normal-navbar/index.jsx
@@ -1,31 +1,25 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { connect } from 'react-redux'
 import { is, fromJS } from 'immutable'
-import { Icon, Popover, Modal, Pagination, notification } from 'antd'
+import { Icon, Popover } from 'antd'
 
-import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
 
 import MKEmitter from '@/utils/events.js'
-import Utils from '@/utils/utils.js'
+// import Utils from '@/utils/utils.js'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
-const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
 const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
-const CardComponent = asyncComponent(() => import('../cardcomponent'))
-const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
+const MenuComponent = asyncIconComponent(() => import('./menusetting'))
+// const CardComponent = asyncComponent(() => import('../cardcomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
-const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
-const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
-const ActionComponent = asyncComponent(() => import('@/menu/components/share/actioncomponent'))
 
-const { confirm } = Modal
+// const { confirm } = Modal
 
-class DataCardEditComponent extends Component {
+class NormalNavbar extends Component {
   static propTpyes = {
     card: PropTypes.object,
     deletecomponent: PropTypes.func,
@@ -46,36 +40,15 @@
         uuid: card.uuid,
         type: card.type,
         floor: card.floor,
-        tabId: card.tabId || '',
-        parentId: card.parentId || '',
-        format: 'array',   // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
-        pageable: true,    // 缁勪欢灞炴�� - 鏄惁鍙垎椤�
-        switchable: true,  // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹�
         dataName: card.dataName || '',
         width: card.width || 24,
         name: card.name,
         subtype: card.subtype,
-        setting: { interType: 'system' },
-        wrap: { name: card.name, width: card.width || 24, title: '', pagestyle: 'page', switch: 'false' },
-        style: { marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px' },
-        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
-        columns: [],
-        scripts: [],
-        action: [],
-        search: [],
-        btnlog: [],
-        subcards: [{
-          uuid: Utils.getuuid(),
-          setting: { width: 6, type: 'simple'},
-          style: {
-            borderWidth: '1px', borderColor: '#e8e8e8',
-            paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
-            marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
-          },
-          backStyle: {},
-          elements: [],
-          backElements: []
-        }]
+        wrap: { name: card.name, width: card.width || 1200 },
+        logoStyle: { width: '100px' },
+        style: { },
+        links: [],
+        menus: [],
       }
 
       if (card.config) {
@@ -84,28 +57,6 @@
         _card.wrap = config.wrap
         _card.wrap.name = card.name
         _card.style = config.style
-        _card.headerStyle = config.headerStyle
-
-        _card.subcards = config.subcards.map(scard => {
-          scard.uuid = Utils.getuuid()
-          scard.elements = scard.elements.map(elem => {
-            elem.uuid = Utils.getuuid()
-            return elem
-          })
-          scard.backElements = scard.backElements.map(elem => {
-            elem.uuid = Utils.getuuid()
-            return elem
-          })
-          return scard
-        })
-        _card.action = config.action.map(col => {
-          col.uuid = Utils.getuuid()
-          return col
-        })
-        _card.search = config.search.map(col => {
-          col.uuid = Utils.getuuid()
-          return col
-        })
       }
 
       this.setState({
@@ -113,9 +64,6 @@
       })
       this.props.updateConfig(_card)
     } else {
-      card.action = card.action || [] // 鍏煎
-      card.search = card.search || [] // 鍏煎
-
       this.setState({
         card: fromJS(card).toJS()
       })
@@ -124,12 +72,10 @@
 
   componentDidMount () {
     MKEmitter.addListener('submitStyle', this.getStyle)
-    MKEmitter.addListener('submitModal', this.handleSave)
-    MKEmitter.addListener('logButton', this.logButton)
   }
 
   shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
+    return !is(fromJS(this.state), fromJS(nextState))
   }
 
   /**
@@ -140,22 +86,6 @@
       return
     }
     MKEmitter.removeListener('submitStyle', this.getStyle)
-    MKEmitter.removeListener('submitModal', this.handleSave)
-    MKEmitter.removeListener('logButton', this.logButton)
-  }
-
-  logButton = (id, item) => {
-    const { card } = this.state
-
-    if (id !== card.uuid) return
-
-    let btnlog = card.btnlog || []
-    btnlog.push(item)
-
-    this.setState({
-      card: {...card, btnlog}
-    })
-    this.props.updateConfig({...card, btnlog})
   }
   
   /**
@@ -188,54 +118,17 @@
     this.props.updateConfig(card)
   }
 
-  /**
-   * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
-   */
-  deleteCard = (cell) => {
-    let card = fromJS(this.state.card).toJS()
-    let _this = this
-
-    confirm({
-      content: '纭畾鍒犻櫎鍗$墖鍚楋紵',
-      onOk() {
-        card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
-
-        let uuids = []
-        cell.elements && cell.elements.forEach(c => {
-          if (c.eleType === 'button') {
-            uuids.push(c.uuid)
-          }
-        })
-        cell.backElements && cell.backElements.forEach(c => {
-          if (c.eleType === 'button') {
-            uuids.push(c.uuid)
-          }
-        })
-        MKEmitter.emit('delButtons', uuids)
-
-        if (card.btnlog) {
-          card.btnlog = card.btnlog.filter(c => c.$parentId !== cell.uuid)
-        }
-
-        _this.setState({card})
-        _this.props.updateConfig(card)
-      },
-      onCancel() {}
-    })
-  }
-
-  changeStyle = () => {
-    const { card } = this.state
-
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
-  }
-
   getStyle = (comIds, style) => {
     const { card } = this.state
 
-    if (comIds.length !== 1 || comIds[0] !== card.uuid) return
+    if (comIds[0] !== card.uuid) return
 
-    let _card = {...card, style}
+    let _card = {...card}
+    if (comIds.length === 1) {
+      _card = {...card, style}
+    } else if (comIds[1] === 'logo') {
+      _card = {...card, logoStyle: style}
+    }
 
     this.setState({
       card: _card
@@ -244,134 +137,16 @@
     this.props.updateConfig(_card)
   }
 
-  addSearch = () => {
+  changeStyle = () => {
     const { card } = this.state
 
-    let newcard = {}
-    newcard.uuid = Utils.getuuid()
-    newcard.focus = true
-
-    newcard.label = 'label'
-    newcard.type = 'select'
-    newcard.resourceType = '0'
-    newcard.options = []
-    newcard.setAll = 'false'
-    newcard.orderType = 'asc'
-    newcard.display = 'dropdown'
-    newcard.match = '='
-
-    // 娉ㄥ唽浜嬩欢-娣诲姞鎼滅储
-    MKEmitter.emit('addSearch', card.uuid, newcard)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'shadow'], card.style)
   }
 
-  addButton = () => {
+  changeLogoStyle = () => {
     const { card } = this.state
 
-    let newcard = {}
-    newcard.uuid = Utils.getuuid()
-    newcard.focus = true
-    
-    newcard.label = 'label'
-    newcard.Ot = 'requiredSgl'
-    newcard.OpenType = 'pop'
-    newcard.icon = ''
-    newcard.class = 'green'
-    newcard.intertype = card.setting.interType || 'system'
-    newcard.innerFunc = card.setting.innerFunc || ''
-    newcard.sysInterface = card.setting.sysInterface || ''
-    newcard.outerFunc = card.setting.outerFunc || ''
-    newcard.interface = card.setting.interface || ''
-    newcard.execSuccess = 'grid'
-    newcard.execError = 'never'
-    newcard.verify = null
-    newcard.show = 'button'
-    newcard.btnstyle = {marginRight: '15px'}
-
-    // 娉ㄥ唽浜嬩欢-娣诲姞鎸夐挳
-    MKEmitter.emit('addButton', card.uuid, newcard)
-  }
-
-  setSubConfig = (item) => {
-    const { card } = this.state
-    let btn = fromJS(item).toJS()
-
-    if (btn.OpenType === 'pop' || btn.execMode === 'pop') {
-      if (!btn.modal) {
-        btn.modal = {
-          setting: { title: btn.label, width: 60, cols: '2', container: 'view', focus: '', finish: 'close', clickouter: 'unclose', display: 'modal' },
-          tables: [],
-          groups: [],
-          fields: []
-        }
-      }
-      MKEmitter.emit('changeModal', card, btn)
-    } else if (btn.OpenType === 'popview') {
-      MKEmitter.emit('changePopview', card, btn)
-    }
-  }
-
-  handleSave = (_cards, btn, modal) => {
-    let card = fromJS(this.state.card).toJS()
-
-    if (card.uuid !== _cards.uuid) return
-
-    let _index = card.action.findIndex(cell => cell.uuid === btn.uuid)
-
-    if (_index === -1) return
-
-    card.action = card.action.map(cell => {
-      if (cell.uuid === btn.uuid) {
-        cell.modal = modal
-      }
-
-      return cell
-    })
-
-    this.setState({card})
-    this.props.updateConfig(card)
-  }
-
-  handleLog = (type, logs, item) => {
-    let card = fromJS(this.state.card).toJS()
-
-    if (type === 'revert') {
-      let done = false
-      if (item.$parentId) {
-        card.subcards.forEach(col => {
-          if (item.$parentId === col.uuid) {
-            if (item.$side !== 'back') {
-              col.elements = col.elements ? [...col.elements, item] : [item]
-            } else {
-              col.backElements = col.backElements ? [...col.backElements, item] : [item]
-            }
-            done = true
-          }
-        })
-      }
-
-      if (!done) {
-        card.action = card.action ? [...card.action, item] : [item]
-      }
-
-      card.btnlog = logs
-
-      this.setState({ card })
-      this.props.updateConfig(card)
-      notification.success({
-        top: 92,
-        message: '鎭㈠鎴愬姛锛�',
-        duration: 2
-      })
-    } else {
-      card.btnlog = logs
-      this.setState({ card })
-      this.props.updateConfig(card)
-      notification.success({
-        top: 92,
-        message: '娓呴櫎鎴愬姛锛�',
-        duration: 2
-      })
-    }
+    MKEmitter.emit('changeStyle', [card.uuid, 'logo'], ['width', 'margin'], card.logoStyle)
   }
 
   clickComponent = (e) => {
@@ -384,54 +159,39 @@
   render() {
     const { card } = this.state
 
-    let offset = 0
-    if (card.wrap.cardFloat && card.wrap.cardFloat !== 'left') {
-      let _width = 0
-      card.subcards.forEach(card => {
-        _width += card.setting.width
-      })
-      offset = _width < 24 ? 24 - _width : 0
-      if (card.wrap.cardFloat === 'center') {
-        offset = Math.floor(offset / 2)
-      }
+    let _style = {...card.style}
+    if (_style.shadow) {
+      _style.boxShadow = '0 0 4px ' + _style.shadow
     }
 
     return (
-      <div className="menu-data-card-edit-box" style={{...card.style}} onClick={this.clickComponent} id={card.uuid}>
-        <NormalHeader defaultshow="hidden" config={card} updateComponent={this.updateComponent}/>
+      <div className="normal-navbar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
-            <Icon className="plus" title="娣诲姞鎼滅储" onClick={this.addSearch} type="plus-circle" />
-            <Icon className="plus" title="娣诲姞鎸夐挳" onClick={this.addButton} type="plus-square" />
+            <MenuComponent config={card} updateConfig={this.updateComponent} />
             <WrapComponent config={card} updateConfig={this.updateComponent} />
-            <CopyComponent type="datacard" card={card}/>
-            <PasteComponent config={card} options={['action', 'search', 'form']} updateConfig={this.updateComponent} />
+            <CopyComponent type="normalnarbar" card={card}/>
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
-            <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog} />
             <UserComponent config={card}/>
             <Icon className="close" title="鍒犻櫎缁勪欢" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
-            <SettingComponent config={card} updateConfig={this.updateComponent} />
           </div>
         } trigger="hover">
           <Icon type="tool" />
         </Popover>
-        <ActionComponent config={card} setSubConfig={this.setSubConfig} updateaction={this.updateComponent}/>
-        {card.subcards.map((subcard, index) => (<CardComponent key={subcard.uuid} offset={!index ? offset : 0} cards={card} card={subcard} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
-        <div style={{clear: 'both'}}></div>
-        {card.wrap.pagestyle !== 'switch' && card.setting.laypage === 'true' ? <Pagination total={85} size="small" showTotal={total => `鍏� ${total} 鏉} pageSize={20} defaultCurrent={1}/> : null}
+        <div className="navbar-wrap" style={{width: card.wrap.width + 'px', height: card.wrap.height + 'px', lineHeight: card.wrap.height + 'px'}}>
+          {card.wrap.logo ? <Popover overlayClassName="mk-popover-control-wrap top-menu-popover" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+            <div className="mk-popover-control">
+              <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeLogoStyle} type="font-colors" />
+            </div>
+          } trigger="hover">
+            <div className="logo" style={card.logoStyle}><img src={card.wrap.logo} alt=""/></div>
+          </Popover> : null}
+          <div className="menu">sdf</div>
+          <div className="link">asdfds</div>
+        </div>
       </div>
     )
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(DataCardEditComponent)
\ No newline at end of file
+export default NormalNavbar
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/index.scss b/src/pc/components/navbar/normal-navbar/index.scss
index 6346310..bdba21d 100644
--- a/src/pc/components/navbar/normal-navbar/index.scss
+++ b/src/pc/components/navbar/normal-navbar/index.scss
@@ -1,12 +1,36 @@
-.menu-data-card-edit-box {
-  position: relative;
+.normal-navbar-edit-box {
+  position: fixed;
+  top: 0px;
+  left: 0px;
+  width: 100%;
   box-sizing: border-box;
   background: #ffffff;
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
-  min-height: 20px;
+  min-height: 50px;
+  z-index: 2;
   
+  .navbar-wrap {
+    margin: 0 auto;
+    display: flex;
+
+    .logo {
+      display: inline-block;
+      img {
+        max-width: 100%;
+        max-height: 100%;
+      }
+    }
+    .menu {
+      flex: 1;
+      display: inline-block;
+    }
+    .link {
+      flex: 1;
+      display: inline-block;
+    }
+  }
   .card-control {
     position: absolute;
     top: 0px;
@@ -21,7 +45,7 @@
     position: absolute;
     z-index: 2;
     font-size: 16px;
-    right: 1px;
+    right: 25px;
     top: 1px;
     cursor: pointer;
     padding: 5px;
@@ -76,12 +100,15 @@
     }
   }
 }
-.menu-data-card-edit-box::after {
+.normal-navbar-edit-box::after {
   display: block;
   content: ' ';
   clear: both;
 }
-.menu-data-card-edit-box:hover {
+.normal-navbar-edit-box:hover {
   z-index: 1;
   box-shadow: 0px 0px 4px #1890ff;
 }
+.top-menu-popover {
+  padding-top: 0!important;
+}
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/index.jsx b/src/pc/components/navbar/normal-navbar/menusetting/index.jsx
new file mode 100644
index 0000000..bb5070f
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/menusetting/index.jsx
@@ -0,0 +1,73 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Icon, Modal } from 'antd'
+
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import MenuTable from './menutable'
+import './index.scss'
+
+class DataSource extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    visible: false
+  }
+
+  UNSAFE_componentWillMount () {
+    const { config } = this.props
+
+    this.setState({menus: fromJS(config.menus).toJS()})
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  verifySubmit = () => {
+    // const { config } = this.props
+
+    // this.verifyRef.handleConfirm().then(res => {
+
+    //   this.setState({
+    //     wrap: res,
+    //     visible: false
+    //   })
+    //   this.props.updateConfig({...config, wrap: res})
+    // })
+  }
+
+  render () {
+    const { config } = this.props
+    const { visible, dict } = this.state
+
+    return (
+      <div className="model-menu-setting-wrap">
+        <Icon type="menu" title="鑿滃崟" onClick={() => this.setState({ visible: true })}/>
+        <Modal
+          wrapClassName="popview-modal"
+          title="鑿滃崟缂栬緫"
+          visible={visible}
+          width={800}
+          maskClosable={false}
+          okText={dict['model.submit']}
+          onOk={this.verifySubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <MenuTable
+            menus={config.menus}
+            ref={(ref) => { this.mTable = ref }}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default DataSource
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/index.scss b/src/pc/components/navbar/normal-navbar/menusetting/index.scss
new file mode 100644
index 0000000..04372e6
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/menusetting/index.scss
@@ -0,0 +1,7 @@
+.model-menu-setting-wrap {
+  display: inline-block;
+
+  >.anticon-edit {
+    color: #1890ff;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx b/src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx
new file mode 100644
index 0000000..bd7216a
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx
@@ -0,0 +1,133 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Radio, Tooltip, Icon } from 'antd'
+
+import './index.scss'
+
+const { TextArea } = Input
+
+class SettingForm extends Component {
+  static propTpyes = {
+    menu: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
+  }
+
+  state = {
+    property: this.props.menu.property || 'menu'
+  }
+
+  UNSAFE_componentWillMount () {
+    
+  }
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          resolve(values)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  handleSubmit = (e) => {
+    e.preventDefault()
+
+    if (this.props.inputSubmit) {
+      this.props.inputSubmit()
+    }
+  }
+
+  changeProperty = (e) => {
+    let val = e.target.value
+
+    this.setState({property: val})
+  }
+
+  render() {
+    const { menu } = this.props
+    const { getFieldDecorator } = this.props.form
+    const { property } = this.state
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout}>
+        <Row gutter={24}>
+          <Col span={24}>
+            <Form.Item label="鑿滃崟鍚嶇О">
+              {getFieldDecorator('name', {
+                initialValue: menu.name,
+                rules: [
+                  {
+                    required: true,
+                    message: '璇疯緭鍏ヨ彍鍗曞悕绉�!'
+                  }
+                ]
+              })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label="灞炴��">
+              {getFieldDecorator('property', {
+                initialValue: menu.property || 'menu'
+              })(
+                <Radio.Group onChange={this.changeProperty}>
+                  <Radio value="menu">鑿滃崟</Radio>
+                  <Radio value="link">閾炬帴</Radio>
+                  <Radio value="classify">鍒嗙被</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          {property === 'link' ? <Col span={24}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="閾炬帴鑷冲綋鍓嶇郴缁熺殑鑿滃崟鏃讹紝鍙互浣跨敤 $ + 鑿滃崟ID锛屼緥濡傦細$dsdffowejdsfi銆�">
+                <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/>
+                閾炬帴鍦板潃
+              </Tooltip>
+            }>
+              {getFieldDecorator('link', {
+                initialValue: menu.link || '',
+                rules: [
+                  {
+                    required: true,
+                    message: '璇疯緭鍏ラ摼鎺ュ湴鍧�!'
+                  }
+                ]
+              })(<TextArea rows={2} />)}
+            </Form.Item>
+          </Col> : null}
+          {property === 'menu' ? <Col span={24}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="澶嶅埗鍏朵粬鑿滃崟鏃讹紝璇峰~鍐欏搴旂殑鑿滃崟ID銆�">
+                <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/>
+                澶嶅埗鑿滃崟
+              </Tooltip>
+            }>
+              {getFieldDecorator('copyMenu', {
+                initialValue: menu.copyMenu || ''
+              })(
+                <Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />
+              )}
+            </Form.Item>
+          </Col> : null}
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/menuform/index.scss b/src/pc/components/navbar/normal-navbar/menusetting/menuform/index.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/menusetting/menuform/index.scss
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx b/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx
new file mode 100644
index 0000000..6d38929
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx
@@ -0,0 +1,168 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Table, Button, Modal } from 'antd'
+
+import MenuForm from '../menuform'
+import Utils from '@/utils/utils.js'
+import './index.scss'
+
+class SubTable extends Component {
+  static propTpyes = {
+    menus: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+  }
+
+  state = {
+    data: [],
+    columns: [
+      { title: 'Date', dataIndex: 'date', key: 'date' },
+      { title: 'Name', dataIndex: 'name', key: 'name' },
+      {
+        title: 'Status',
+        key: 'state',
+        render: () => (
+          <span>
+            Finished
+          </span>
+        ),
+      },
+      { title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
+      {
+        title: 'Action',
+        dataIndex: 'operation',
+        key: 'operation',
+        render: () => (
+          <span className="table-operation">
+            <a href>Pause</a>
+            <a href>Stop</a>
+          </span>
+        ),
+      },
+    ]
+  }
+
+  UNSAFE_componentWillMount () {
+    // const { data } = this.props
+
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  handleSubmit = (e) => {
+    e.preventDefault()
+
+    if (this.props.inputSubmit) {
+      this.props.inputSubmit()
+    }
+  }
+
+  render() {
+    const { columns, data } = this.state
+
+    return (
+      <Table
+        className="components-table-demo-nested"
+        columns={columns}
+        dataSource={data}
+      />
+    )
+  }
+}
+
+class MenuTable extends Component {
+  static propTpyes = {
+    menus: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+  }
+
+  state = {
+    data: [],
+    editMenu: null,
+    columns: [
+      { title: '鑿滃崟鍚嶇О', dataIndex: 'name', key: 'name' },
+      { title: '灞炴��', dataIndex: 'property', key: 'property',  render: text => {
+        if (text === 'menu') {
+          return '鑿滃崟'
+        } else {
+          return '鍒嗙被'
+        }
+      }},
+      { title: 'Action', key: 'operation', render: () => <a href="#d">Publish</a> },
+    ]
+  }
+
+  UNSAFE_componentWillMount () {
+    const { menus } = this.props
+
+    this.setState({data: fromJS(menus).toJS()})
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  plusMenu = () => {
+    let _menu = {
+      name: '鑿滃崟',
+      property: 'classify',
+      level: 1,
+      children: []
+    }
+
+    this.setState({editMenu: _menu, visible: true})
+  }
+
+  menuSubmit = () => {
+    const { editMenu, data } = this.state
+
+    this.menuRef.handleConfirm().then(res => {
+      let _menu = {...editMenu, ...res}
+      if (!_menu.uuid) {
+        _menu.uuid = Utils.getuuid()
+        this.setState({data: [...data, _menu]})
+      } else {
+        this.setState({data: data.map(item => {
+          if (item.uuid === _menu.uuid) {
+            return _menu
+          } else {
+            return item
+          }
+        })})
+      }
+    })
+  }
+
+  render() {
+    const { columns, data, visible, editMenu } = this.state
+
+    return (
+      <div className="menu-control-wrap">
+        <Button className="menu-plus mk-green" onClick={this.plusMenu}>娣诲姞</Button>
+        <Table
+          className="components-table-demo-nested"
+          columns={columns}
+          expandedRowRender={<SubTable />}
+          dataSource={data}
+        />
+        <Modal
+          title="缂栬緫"
+          visible={visible}
+          width={600}
+          maskClosable={false}
+          onOk={this.menuSubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <MenuForm
+            menu={editMenu}
+            inputSubmit={this.menuSubmit}
+            wrappedComponentRef={(inst) => this.menuRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default MenuTable
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.scss b/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.scss
new file mode 100644
index 0000000..1a060dd
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.scss
@@ -0,0 +1,10 @@
+.menu-control-wrap {
+  position: relative;
+
+  .menu-plus {
+    float: right;
+    position: relative;
+    z-index: 1;
+    margin-bottom: 5px;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/wrapsetting/index.jsx b/src/pc/components/navbar/normal-navbar/wrapsetting/index.jsx
new file mode 100644
index 0000000..81346a6
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/wrapsetting/index.jsx
@@ -0,0 +1,83 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Icon, Modal } from 'antd'
+
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import SettingForm from './settingform'
+import './index.scss'
+
+class DataSource extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    visible: false,
+    wrap: null
+  }
+
+  UNSAFE_componentWillMount () {
+    const { config } = this.props
+
+    this.setState({wrap: fromJS(config.wrap).toJS()})
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  editDataSource = () => {
+    this.setState({
+      visible: true
+    })
+  }
+
+  verifySubmit = () => {
+    const { config } = this.props
+
+    this.verifyRef.handleConfirm().then(res => {
+
+      this.setState({
+        wrap: res,
+        visible: false
+      })
+      this.props.updateConfig({...config, wrap: res})
+    })
+  }
+
+  render () {
+    const { config } = this.props
+    const { visible, dict, wrap } = this.state
+
+    return (
+      <div className="model-menu-setting-wrap">
+        <Icon type="edit" title="缂栬緫" onClick={() => this.editDataSource()} />
+        <Modal
+          wrapClassName="popview-modal"
+          title={config.type === 'table' ? '琛ㄦ牸璁剧疆' : '鍗$墖璁剧疆'}
+          visible={visible}
+          width={800}
+          maskClosable={false}
+          okText={dict['model.submit']}
+          onOk={this.verifySubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <SettingForm
+            dict={dict}
+            wrap={wrap}
+            config={config}
+            inputSubmit={this.verifySubmit}
+            wrappedComponentRef={(inst) => this.verifyRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default DataSource
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/wrapsetting/index.scss b/src/pc/components/navbar/normal-navbar/wrapsetting/index.scss
new file mode 100644
index 0000000..04372e6
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/wrapsetting/index.scss
@@ -0,0 +1,7 @@
+.model-menu-setting-wrap {
+  display: inline-block;
+
+  >.anticon-edit {
+    color: #1890ff;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx b/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx
new file mode 100644
index 0000000..3a72098
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx
@@ -0,0 +1,137 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Tooltip, Icon, InputNumber } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
+
+class SettingForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,      // 瀛楀吀椤�
+    config: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+    wrap: PropTypes.object,      // 鏁版嵁婧愰厤缃�
+    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
+  }
+
+  state = {
+    roleList: []
+  }
+
+  UNSAFE_componentWillMount () {
+    let roleList = sessionStorage.getItem('sysRoles')
+    if (roleList) {
+      try {
+        roleList = JSON.parse(roleList)
+      } catch {
+        roleList = []
+      }
+    } else {
+      roleList = []
+    }
+
+    this.setState({roleList})
+  }
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          resolve(values)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  handleSubmit = (e) => {
+    e.preventDefault()
+
+    if (this.props.inputSubmit) {
+      this.props.inputSubmit()
+    }
+  }
+
+  render() {
+    const { wrap } = this.props
+    const { getFieldDecorator } = this.props.form
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <div className="model-menu-setting-form">
+        <Form {...formItemLayout}>
+          <Row gutter={24}>
+            <Col span={12}>
+              <Form.Item label="瀵艰埅鏍忓悕绉�">
+                {getFieldDecorator('name', {
+                  initialValue: wrap.name,
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.input'] + '瀵艰埅鏍忓悕绉�!'
+                    }
+                  ]
+                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="瀵艰埅鏍忎富浣撳唴瀹瑰搴︼紙鍖呮嫭logo銆佽彍鍗曘�侀摼鎺ョ瓑锛夈��">
+                  <Icon type="question-circle" />
+                  瀹藉害
+                </Tooltip>
+              }>
+                {getFieldDecorator('width', {
+                  initialValue: wrap.width || 1200,
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.input'] + '瀹藉害!'
+                    }
+                  ]
+                })(<InputNumber min={400} precision={0} onPressEnter={this.handleSubmit} />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label="楂樺害">
+                {getFieldDecorator('height', {
+                  initialValue: wrap.height || 50,
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.input'] + '楂樺害!'
+                    }
+                  ]
+                })(<InputNumber min={50} max={200} precision={0} onPressEnter={this.handleSubmit} />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label="logo">
+                {getFieldDecorator('logo', {
+                  initialValue: wrap.logo
+                })(
+                  <SourceComponent type="image" />
+                )}
+              </Form.Item>
+            </Col>
+          </Row>
+        </Form>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss b/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss
new file mode 100644
index 0000000..159130b
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss
@@ -0,0 +1,11 @@
+.model-menu-setting-form {
+  position: relative;
+
+  .anticon-question-circle {
+    color: #c49f47;
+    margin-right: 3px;
+  }
+  .ant-input-number {
+    width: 100%;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/menushell/card.jsx b/src/pc/menushell/card.jsx
new file mode 100644
index 0000000..06beb23
--- /dev/null
+++ b/src/pc/menushell/card.jsx
@@ -0,0 +1,90 @@
+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 AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
+const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
+const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
+const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
+const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
+const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
+const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
+const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
+const NormalNavbar = asyncComponent(() => import('@/pc/components/navbar/normal-navbar'))
+
+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}
+  }
+  let col = ' ant-col ant-col-' + (card.width || 24)
+  if (card.type === 'navbar') {
+    col = ''
+  }
+
+  const getCardComponent = () => {
+    if (card.type === 'bar' || card.type === 'line') {
+      return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'navbar') {
+      return (<NormalNavbar 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 === '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 === '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 === 'code') {
+      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    }
+  }
+  return (
+    <div className={`mk-component-card ${col}`} ref={node => drag(drop(node))} style={style}>
+      {getCardComponent()}
+    </div>
+  )
+}
+export default Card
diff --git a/src/pc/menushell/index.jsx b/src/pc/menushell/index.jsx
new file mode 100644
index 0000000..714e6c9
--- /dev/null
+++ b/src/pc/menushell/index.jsx
@@ -0,0 +1,165 @@
+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 '@/menu/utils/menuUtils.js'
+import Card from './card'
+import './index.scss'
+
+const { confirm } = Modal
+
+const Container = ({menu, handleList }) => {
+  const [cards, setCards] = useState(menu.components)
+  const moveCard = (id, atIndex) => {
+    const { card, index } = findCard(id)
+    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
+    handleList({...menu, components: _cards})
+  }
+
+  if (!is(fromJS(cards), fromJS(menu.components))) {
+    setCards(menu.components)
+  }
+  
+  const findCard = id => {
+    const card = cards.filter(c => `${c.uuid}` === id)[0]
+    return {
+      card,
+      index: cards.indexOf(card),
+    }
+  }
+
+  const updateConfig = (element) => {
+    handleList({...menu, 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() {
+        MKEmitter.emit('delButtons', uuids)
+        handleList({...menu, components: cards.filter(item => item.uuid !== card.uuid)})
+      },
+      onCancel() {}
+    })
+  }
+
+  const [, drop] = useDrop({
+    accept: 'menu',
+    drop(item) {
+      if (item.hasOwnProperty('originalIndex') || item.added) {
+        delete item.added // 鍒犻櫎缁勪欢娣诲姞鏍囪
+        return
+      }
+
+      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 === 'navbar') {
+        if (cards.filter(card => card.type === 'navbar').length > 0) {
+          notification.warning({
+            top: 92,
+            message: '瀵艰埅鏍忎笉鍙噸澶嶆坊鍔狅紒',
+            duration: 5
+          })
+          return
+        }
+      }
+
+      let name = ''
+      let names = {
+        bar: '鏌辩姸鍥�',
+        line: '鎶樼嚎鍥�',
+        tabs: '鏍囩缁�',
+        pie: '楗煎浘',
+        search: '鎼滅储',
+        table: '琛ㄦ牸',
+        group: '鍒嗙粍',
+        editor: '瀵屾枃鏈�',
+        code: '鑷畾涔�',
+        navbar: '瀵艰埅鏍�',
+        card: '鍗$墖'
+      }
+      let i = 1
+      
+      while (!name && names[item.component]) {
+        let _name = names[item.component] + i
+        if (menu.components.filter(com => com.name === _name).length === 0) {
+          name = _name
+        }
+        i++
+      }
+
+      let newcard = {
+        uuid: Utils.getuuid(),
+        type: item.component,
+        subtype: item.subtype,
+        config: item.config,
+        width: item.width || 24,
+        dataName: Utils.getdataName(),
+        name: name,
+        floor: 1,   // 缁勪欢鐨勫眰绾�
+        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({...menu, components: _cards})
+    }
+  })
+
+  return (
+    <div ref={drop} className="menu-shell-inner" id="menu-shell-inner" style={menu.style}>
+      <div className="ant-row">
+        {cards.map(card => (
+          <Card
+            id={card.uuid}
+            key={card.uuid}
+            card={card}
+            moveCard={moveCard}
+            delCard={deleteCard}
+            findCard={findCard}
+            updateConfig={updateConfig}
+          />
+        ))}
+      </div>
+      {cards.length === 0 ?
+        <Empty description="璇锋坊鍔犵粍浠�" /> : null
+      }
+    </div>
+  )
+}
+export default Container
diff --git a/src/pc/menushell/index.scss b/src/pc/menushell/index.scss
new file mode 100644
index 0000000..21f8a7d
--- /dev/null
+++ b/src/pc/menushell/index.scss
@@ -0,0 +1,23 @@
+.menu-shell-inner {
+  min-height: calc(100vh - 100px);
+  width: 100%;
+  background-size: 100%;
+
+  .anticon {
+    cursor: unset;
+  }
+
+  .mk-component-card {
+    position: relative;
+  }
+  
+  >.ant-empty {
+    padding-top: 150px;
+  }
+  .anticon-tool {
+    color: rgba(0, 0, 0, 0.55);
+  }
+  .anticon-tool:hover {
+    color: #1890ff;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/modulesource/option.jsx b/src/pc/modulesource/option.jsx
index 9c9ee1c..81ce70d 100644
--- a/src/pc/modulesource/option.jsx
+++ b/src/pc/modulesource/option.jsx
@@ -14,9 +14,11 @@
 import Pie1 from '@/assets/mobimg/ring.png'
 import Pie2 from '@/assets/mobimg/nightingale.png'
 import Mainsearch from '@/assets/mobimg/mainsearch.png'
+import Navbar from '@/assets/mobimg/navbar.png'
 
 // 缁勪欢閰嶇疆淇℃伅
 export const menuOptions = [
+  { type: 'menu', url: Navbar, component: 'navbar', subtype: 'navbar', title: '瀵艰埅鏍�', width: 1200 },
   { type: 'menu', url: tabs, component: 'tabs', subtype: 'tabs', title: '鏍囩椤�', width: 24 },
   { type: 'menu', url: Mainsearch, component: 'search', subtype: 'mainsearch', title: '鎼滅储鏉′欢', width: 24 },
   { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '鏁版嵁鍗�', width: 24 },
diff --git a/src/tabviews/custom/index.jsx b/src/tabviews/custom/index.jsx
index 01b346d..1cf5080 100644
--- a/src/tabviews/custom/index.jsx
+++ b/src/tabviews/custom/index.jsx
@@ -414,6 +414,16 @@
           return false
         }
 
+        item.subtabs = item.subtabs.filter(tab => {
+          if (
+            tab.blacklist && tab.blacklist.length > 0 &&
+            tab.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0
+          ) {
+            return false
+          }
+          return true
+        })
+
         item.subtabs = item.subtabs.map(tab => {
           tab.components = this.filterComponent(tab.components, roleId, permAction, permMenus)
           return tab
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
index 3a5c5d5..c733f72 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
@@ -415,14 +415,14 @@
       <div className="modal-menu-setting-script">
         <Form {...formItemLayout}>
           <Row gutter={24}>
-            <Col span={8}>
-              <Form.Item label={'鍥炶皟琛ㄥ悕'} style={{whiteSpace: 'nowrap', margin: 0}}>
+            <Col span={4}>
+              <Form.Item labelCol={{span: 17}} wrapperCol={{span: 7}} label={'鍥炶皟琛ㄥ悕'} style={{whiteSpace: 'nowrap', margin: 0}}>
                 {setting.cbTable}
               </Form.Item>
             </Col>
-            <Col span={16}>
-              <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0}}>
-                ErrorCode, retmsg
+            <Col span={20}>
+              <Form.Item labelCol={{span: 4}} wrapperCol={{span: 20}} label={'鎶ラ敊瀛楁'} style={{margin: 0}}>
+                ErrorCode锛堝鍔犲悗缂�NT琛ㄧず鏁版嵁涓嶅洖婊氾紝濡侲NT銆丯NT銆丗NT銆丯MNT锛�, retmsg
               </Form.Item>
             </Col>
             {usefulFields ? <Col span={24} className="sqlfield">
diff --git a/src/views/pcdesign/index.jsx b/src/views/pcdesign/index.jsx
index 9628725..959975f 100644
--- a/src/views/pcdesign/index.jsx
+++ b/src/views/pcdesign/index.jsx
@@ -5,10 +5,8 @@
 import moment from 'moment'
 import HTML5Backend from 'react-dnd-html5-backend'
 import { ConfigProvider, notification, Modal, Collapse, Switch, Button, Icon } from 'antd'
-// import html2canvas from 'html2canvas'
 
 import Api from '@/api'
-// import options from '@/store/options.js'
 import Utils from '@/utils/utils.js'
 import zhCN from '@/locales/zh-CN/mob.js'
 import enUS from '@/locales/en-US/mob.js'
@@ -25,7 +23,7 @@
 const { confirm } = Modal
 
 const MenuForm = asyncComponent(() => import('./menuform'))
-const MenuShell = asyncComponent(() => import('@/menu/menushell'))
+const MenuShell = asyncComponent(() => import('@/pc/menushell'))
 const SourceWrap = asyncComponent(() => import('@/pc/modulesource'))
 const BgController = asyncComponent(() => import('@/pc/bgcontroller'))
 const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
@@ -40,6 +38,7 @@
 
 sessionStorage.setItem('isEditState', 'true')
 sessionStorage.setItem('editMenuType', 'menu') // 缂栬緫鑿滃崟绫诲瀷
+sessionStorage.setItem('appType', 'pc')        // 搴旂敤绫诲瀷
 document.body.className = ''
 window.GLOB.UserComponentMap = new Map() // 缂撳瓨鐢ㄦ埛鑷畾涔夌粍浠�
 

--
Gitblit v1.8.0