From 000ff61dd8a88eb875048e6a3deca8679d75df18 Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期六, 29 八月 2020 11:29:33 +0800
Subject: [PATCH] 2020-08-29

---
 src/components/tabview/index.jsx                               |   14 
 src/menu/components/chart/antv-bar/chartcompile/index.jsx      |   45 -
 src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx |   11 
 src/menu/components/chart/antv-bar/chartcompile/index.scss     |   24 
 src/menu/menuform/index.jsx                                    |    4 
 src/assets/mobimg/line.png                                     |    0 
 src/tabviews/zshare/cardcomponent/index.scss                   |   12 
 src/menu/components/chart/antv-bar/index.scss                  |    4 
 src/menu/components/chart/antv-bar/index.jsx                   |   53 ++
 src/menu/modelsource/option.jsx                                |    4 
 src/tabviews/custom/index.jsx                                  |  630 +++++++++++++++++++++++++++++++++++
 src/tabviews/custom/index.scss                                 |   58 +++
 src/menu/menushell/card.jsx                                    |    2 
 src/components/sidemenu/index.jsx                              |    9 
 src/menu/datasourcecomponent/verifycard/settingform/index.jsx  |    2 
 src/menu/header/index.jsx                                      |   46 +-
 src/utils/option.js                                            |   16 
 src/views/menudesign/index.jsx                                 |  122 +++++-
 src/assets/mobimg/line1.png                                    |    0 
 19 files changed, 928 insertions(+), 128 deletions(-)

diff --git a/src/assets/mobimg/line.png b/src/assets/mobimg/line.png
new file mode 100644
index 0000000..55a628e
--- /dev/null
+++ b/src/assets/mobimg/line.png
Binary files differ
diff --git a/src/assets/mobimg/line1.png b/src/assets/mobimg/line1.png
new file mode 100644
index 0000000..b3a44b0
--- /dev/null
+++ b/src/assets/mobimg/line1.png
Binary files differ
diff --git a/src/components/sidemenu/index.jsx b/src/components/sidemenu/index.jsx
index cb2c6a1..a463c20 100644
--- a/src/components/sidemenu/index.jsx
+++ b/src/components/sidemenu/index.jsx
@@ -130,7 +130,7 @@
               try {
                 _tmenu.PageParam = JSON.parse(child.PageParam)
               } catch (e) {
-                _tmenu.PageParam = {}
+                _tmenu.PageParam = {OpenType: 'newtab'}
               }
               _tmenu.type = _tmenu.PageParam.Template || _tmenu.type
             } else {
@@ -142,6 +142,7 @@
             _tmenu.MenuNo = child.MenuNo
             _tmenu.MenuName = child.MenuName
             _tmenu.text = child.MenuName
+            _tmenu.OpenType = _tmenu.PageParam ? _tmenu.PageParam.OpenType : 'newtab'
             return _tmenu
           })
         }
@@ -204,9 +205,11 @@
     }
     
     let menu = JSON.parse(e.target.dataset.item)
-    if (menu.Ot === 'NewPage') {
+
+    if (menu.OpenType === 'newpage') {
+      e.preventDefault()
       window.open(menu.src)
-    } else if (menu.Ot === 'blank') {
+    } else if (menu.OpenType === 'blank') {
       menu.selected = true
       this.props.modifyTabview([menu])
       e.preventDefault()
diff --git a/src/components/tabview/index.jsx b/src/components/tabview/index.jsx
index 7079a93..05c86cc 100644
--- a/src/components/tabview/index.jsx
+++ b/src/components/tabview/index.jsx
@@ -18,6 +18,7 @@
 import './index.scss'
 
 const Home = asyncComponent(() => import('@/tabviews/home'))
+const CustomPage = asyncComponent(() => import('@/tabviews/custom'))
 const CommonTable = asyncComponent(() => import('@/tabviews/commontable'))
 const CalendarPage = asyncComponent(() => import('@/tabviews/calendar'))
 const TreePage = asyncComponent(() => import('@/tabviews/treepage'))
@@ -104,6 +105,8 @@
       return (<Home MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
     } else if (view.type === 'CommonTable' || view.type === 'ManageTable') {
       return (<CommonTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+    } else if (view.type === 'CustomPage') {
+      return (<CustomPage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
     } else if (view.type === 'TreePage') {
       return (<TreePage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
     } else if (view.type === 'CalendarPage') {
@@ -196,6 +199,15 @@
     }
   }
 
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
   render () {
     const { tabviews } = this.state
     let view = tabviews.filter(tab => tab.selected)[0]
@@ -213,7 +225,7 @@
                     <Tabs.TabPane
                       tab={
                         <span className="tab-control">
-                          {['CommonTable', 'FormTab', 'TreePage', 'CalendarPage'].includes(view.type) ?
+                          {['CommonTable', 'FormTab', 'TreePage', 'CalendarPage', 'CustomPage'].includes(view.type) ?
                             <Icon type="redo" onClick={(e) => {this.refreshTabview(e, view)}}/> : null
                           }
                           <span className="tab-name" onClick={(e) => {this.changeTab(e, view)}}>
diff --git a/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx b/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
index 677001d..66ed435 100644
--- a/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
+++ b/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
@@ -33,6 +33,9 @@
     ]
   }
 
+  let xfields = columns.filter(item => /^Nvarchar/ig.test(item.datatype))
+  let yfields = columns.filter(item => /^(Int|Decimal)/ig.test(item.datatype))
+
   return [
     {
       type: 'radio',
@@ -52,7 +55,7 @@
       label: 'X-杞�',
       initVal: card.Xaxis || '',
       required: true,
-      options: columns.filter(col => col.type === 'text')
+      options: xfields
     },
     {
       type: 'select',
@@ -62,7 +65,7 @@
       multi: true, // 澶氶��
       hidden: card.datatype === 'statistics',
       required: true,
-      options: columns.filter(col => col.type === 'number')
+      options: yfields
     },
     {
       type: 'select',
@@ -71,7 +74,7 @@
       initVal: card.InfoType || '',
       hidden: card.datatype !== 'statistics',
       required: true,
-      options: columns.filter(col => col.type === 'text')
+      options: xfields
     },
     {
       type: 'select',
@@ -80,7 +83,7 @@
       initVal: card.InfoValue || '',
       hidden: card.datatype !== 'statistics',
       required: true,
-      options: columns.filter(col => col.type === 'number')
+      options: yfields
     },
     {
       type: 'select',
diff --git a/src/menu/components/chart/antv-bar/chartcompile/index.jsx b/src/menu/components/chart/antv-bar/chartcompile/index.jsx
index e40a2ed..315d9bd 100644
--- a/src/menu/components/chart/antv-bar/chartcompile/index.jsx
+++ b/src/menu/components/chart/antv-bar/chartcompile/index.jsx
@@ -23,7 +23,6 @@
     disabled: true,
     plot: null,
     formlist: null,
-    datatype: null,
     fieldName: null,
     colorOptions: fromJS(minkeColorSystem).toJS().map(option => {
       option.children = option.children.map(cell => {
@@ -78,6 +77,7 @@
     this.setState({
       visible: true,
       view: 'normal',
+      disabled: config.plot.datatype === 'statistics',
       fieldName: fieldName,
       plot: fromJS(config.plot).toJS(),
       formlist: getBarOrLineChartOptionForm(config.plot, config.columns, config.setting)
@@ -90,7 +90,7 @@
 
     if (key === 'datatype') {
       this.setState({
-        datatype: val,
+        disabled: val === 'statistics',
         formlist: formlist.map(item => {
           if (['Yaxis'].includes(item.key)) {
             item.hidden = val === 'statistics'
@@ -336,6 +336,7 @@
   }
 
   onSubmit = () => {
+    const { config } = this.props
     const { plot, view, datatype } = this.state
 
     if (view !== 'custom') {
@@ -360,7 +361,7 @@
             visible: false
           })
 
-          this.props.plotchange(_plot)
+          this.props.plotchange({...config, plot: _plot})
         }
       })
     } else {
@@ -400,12 +401,11 @@
     }
   }
 
-  changeView = () => {
+  changeTab = (tab) => {
     const { config } = this.props
-    const { plot, view } = this.state
-    let _view = view === 'normal' ? 'custom' : 'normal'
+    const { plot } = this.state
 
-    if (_view === 'custom') {
+    if (tab === 'custom') {
       this.props.form.validateFieldsAndScroll((err, values) => {
         if (!err) {
           let _plot = {...plot, ...values}
@@ -426,7 +426,7 @@
           this.setState({
             enabled: _plot.enabled || 'false',
             plot: _plot,
-            view: _view
+            view: tab
           })
         }
       })
@@ -462,15 +462,12 @@
 
           this.setState({
             plot: _plot,
-            view: _view,
+            view: tab,
             formlist: getBarOrLineChartOptionForm(_plot, config.columns)
           })
         }
       })
     }
-  }
-
-  changeTab = () => {
 
   }
 
@@ -499,32 +496,22 @@
           maskClosable={false}
           okText={dict['model.submit']}
           cancelText={dict['model.cancel']}
-          onOk={this.verifySubmit}
+          onOk={this.onSubmit}
           onCancel={() => { this.setState({ visible: false }) }}
           destroyOnClose
         >
           <Tabs activeKey={view} className="menu-chart-edit-box" onChange={this.changeTab}>
             <TabPane tab="鍩虹璁剧疆" key="normal">
-              <Form {...formItemLayout}>
+              {view === 'normal' ? <Form {...formItemLayout}>
                 <Row gutter={16}>{this.getFields()}</Row>
-              </Form>
+              </Form> : null}
             </TabPane>
-            <TabPane tab="瀛楁闆�" disabled={disabled} key="custom">
-              
+            <TabPane tab="鑷畾涔夎缃�" disabled={disabled} key="custom">
+              {view === 'custom' ? <Form {...formItemLayout}>
+                <Row gutter={16}>{this.getCustomFields()}</Row>
+              </Form> : null}
             </TabPane>
           </Tabs>
-          {/* {view !== 'custom' ? <Form {...formItemLayout} className="base-setting">
-            <Row gutter={16}>{this.getFields()}</Row>
-            {datatype === 'query' ? <Row gutter={16}>
-              <Button onClick={this.changeView} style={{border: 0, boxShadow: 'unset',float: 'right', color: '#1890ff', marginRight: 12, cursor: 'pointer'}}>鑷畾涔夎缃�<Icon style={{marginLeft: 5}} type="right" /></Button>
-            </Row> : null}
-          </Form> : null}
-          {view === 'custom' ? <Form {...formItemLayout} id="chart-custom-drawer-form" className="mingke-table">
-            <Row gutter={16} style={{minHeight: 'calc(100vh - 180px)'}}>{this.getCustomFields()}</Row>
-            <Row gutter={16}>
-              <Button onClick={this.changeView} style={{border: 0, boxShadow: 'unset', color: '#1890ff', marginRight: 12, cursor: 'pointer'}}><Icon style={{marginRight: 5}} type="left" />鍩烘湰璁剧疆</Button>
-            </Row>
-          </Form> : null} */}
         </Modal>
       </div>
     );
diff --git a/src/menu/components/chart/antv-bar/chartcompile/index.scss b/src/menu/components/chart/antv-bar/chartcompile/index.scss
index 7338884..517b9a5 100644
--- a/src/menu/components/chart/antv-bar/chartcompile/index.scss
+++ b/src/menu/components/chart/antv-bar/chartcompile/index.scss
@@ -10,16 +10,20 @@
   }
 }
 .menu-chart-edit-modal {
-  .ant-modal-body {
-    padding-top: 10px;
-    .menu-chart-edit-box {
-      .anticon-question-circle {
-        color: #c49f47;
-        position: relative;
-        left: -3px;
-      }
-      .ant-input-number {
-        width: 100%;
+  .ant-modal {
+    top: 50px;
+    .ant-modal-body {
+      max-height: calc(100vh - 190px);
+      padding-top: 10px;
+      .menu-chart-edit-box {
+        .anticon-question-circle {
+          color: #c49f47;
+          position: relative;
+          left: -3px;
+        }
+        .ant-input-number {
+          width: 100%;
+        }
       }
     }
   }
diff --git a/src/menu/components/chart/antv-bar/index.jsx b/src/menu/components/chart/antv-bar/index.jsx
index 4d2f9fa..76cdec7 100644
--- a/src/menu/components/chart/antv-bar/index.jsx
+++ b/src/menu/components/chart/antv-bar/index.jsx
@@ -31,13 +31,14 @@
   }
 
   UNSAFE_componentWillMount () {
-    const { card } = this.props
+    const { card, config } = this.props
 
     if (card.isNew) {
       let _plot = {
         chartType: card.type, // 鍥捐〃绫诲瀷
         enabled: 'false',     // 鏄惁浣跨敤鑷畾涔夎缃�
         datatype: 'query',    // 鏁版嵁绫诲瀷鏌ヨ鎴栫粺璁�
+        customs: []
       }
 
       if (card.subtype === 'bar') {
@@ -46,13 +47,32 @@
       } else if (card.subtype === 'bar1') {
         _plot.coordinate = 'angle'
         _plot.transpose = 'true'
+      } else if (card.subtype === 'line') {
+        _plot.shape = 'smooth'
+      } else if (card.subtype === 'line1') {
+        _plot.shape = 'hv'
+      }
+
+      let name = ''
+      let names = {
+        bar: '鏌辩姸鍥�',
+        line: '鎶樼嚎鍥�',
+      }
+      let i = 1
+      
+      while (!name) {
+        let _name = names[card.type] + i
+        if (config.components.filter(com => com.setting && com.setting.name === _name).length === 0) {
+          name = _name
+        }
+        i++
       }
 
       let _card = {
         uuid: card.uuid,
         type: card.type,
         subtype: card.subtype,
-        setting: {span: 12, height: 400},
+        setting: {span: 12, height: 400, name},
         columns: [],
         scripts: [],
         search: [],
@@ -76,9 +96,7 @@
 
   UNSAFE_componentWillReceiveProps (nextProps) {
     if (!is(fromJS(this.props.plot), fromJS(nextProps.plot))) {
-      this.setState({}, () => {
-        this.viewrender()
-      })
+
     }
   }
 
@@ -117,10 +135,11 @@
   }
 
   linerender = () => {
-    const { plot, config } = this.props
+    const { card } = this.state
+    let plot = {...card.plot, height: card.setting.height - 70}
 
     let transfield = {}
-    config.columns.forEach(col => {
+    card.columns.forEach(col => {
       if (col.field) {
         transfield[col.field] = col.label
       }
@@ -153,7 +172,7 @@
       }
 
       const chart = new Chart({
-        container: plot.uuid,
+        container: card.uuid,
         autoFit: true,
         height: plot.height || 400
       })
@@ -509,8 +528,24 @@
   }
 
   updateComponent = (component) => {
+    const card = fromJS(this.state.card).toJS()
+    let refresh = false
+    if (card.setting.span !== component.setting.span || card.setting.height !== component.setting.height || !is(fromJS(component.plot), fromJS(card.plot))) {
+      let _element = document.getElementById(card.uuid)
+      if (_element) {
+        _element.innerHTML = ''
+      }
+      refresh = true
+    }
+    
     this.setState({
       card: component
+    }, () => {
+      if (refresh) {
+        setTimeout(() => {
+          this.viewrender()
+        }, 100)
+      }
     })
     this.props.updateConfig(component)
   }
@@ -520,7 +555,7 @@
     const { config } = this.props
 
     return (
-      <div className="line-chart-edit-box" style={{height: card.setting.height || 400}}>
+      <div className="menu-line-chart-edit-box" style={{height: card.setting.height || 400}}>
         <SettingComponent
           config={{...card, tables: config.tables}}
           MenuID={config.uuid}
diff --git a/src/menu/components/chart/antv-bar/index.scss b/src/menu/components/chart/antv-bar/index.scss
index c69327d..e96a3a6 100644
--- a/src/menu/components/chart/antv-bar/index.scss
+++ b/src/menu/components/chart/antv-bar/index.scss
@@ -1,6 +1,6 @@
-.line-chart-edit-box {
+.menu-line-chart-edit-box {
   position: relative;
-  margin-bottom: 30px;
+  // margin-bottom: 0px;
   border: 1px solid #e8e8e8;
   
   .canvas {
diff --git a/src/menu/datasourcecomponent/verifycard/settingform/index.jsx b/src/menu/datasourcecomponent/verifycard/settingform/index.jsx
index 2cbec37..5458009 100644
--- a/src/menu/datasourcecomponent/verifycard/settingform/index.jsx
+++ b/src/menu/datasourcecomponent/verifycard/settingform/index.jsx
@@ -286,7 +286,7 @@
               }>
                 {getFieldDecorator('height', {
                   initialValue: setting.height || 400
-                })(<InputNumber min={100} max={1500} precision={0} />)}
+                })(<InputNumber min={150} max={1500} precision={0} />)}
               </Form.Item>
             </Col>
             {interType === 'inner' ? <Col span={8}>
diff --git a/src/menu/header/index.jsx b/src/menu/header/index.jsx
index 1e9ae6d..e8ab7dc 100644
--- a/src/menu/header/index.jsx
+++ b/src/menu/header/index.jsx
@@ -3,7 +3,7 @@
 import { is, fromJS } from 'immutable'
 import { connect } from 'react-redux'
 import { withRouter } from 'react-router-dom'
-import {Dropdown, Menu, Icon, Modal } from 'antd'
+// import { Icon, Modal } from 'antd'
 
 import { logout } from '@/store/action'
 import zhCN from '@/locales/zh-CN/mob.js'
@@ -11,7 +11,7 @@
 import avatar from '@/assets/img/avatar.jpg'
 import './index.scss'
 
-const { confirm } = Modal
+// const { confirm } = Modal
 
 class MobHeader extends Component {
   static propTpyes = {
@@ -27,22 +27,22 @@
     userName: sessionStorage.getItem('CloudUserName')
   }
 
-  logout = () => {
-    // 閫�鍑虹櫥褰�
-    let _this = this
-    confirm({
-      title: this.state.dict['mob.logout.hint'],
-      content: '',
-      okText: this.state.dict['mob.confirm'],
-      cancelText: this.state.dict['mob.cancel'],
-      onOk() {
-        sessionStorage.clear()
-        _this.props.logout()
-        _this.props.history.replace('/login')
-      },
-      onCancel() {}
-    })
-  }
+  // logout = () => {
+  //   // 閫�鍑虹櫥褰�
+  //   let _this = this
+  //   confirm({
+  //     title: this.state.dict['mob.logout.hint'],
+  //     content: '',
+  //     okText: this.state.dict['mob.confirm'],
+  //     cancelText: this.state.dict['mob.cancel'],
+  //     onOk() {
+  //       sessionStorage.clear()
+  //       _this.props.logout()
+  //       _this.props.history.replace('/login')
+  //     },
+  //     onCancel() {}
+  //   })
+  // }
 
   shouldComponentUpdate (nextProps, nextState) {
     return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
@@ -75,18 +75,18 @@
     return (
       <header className="menu-header-container">
         <div className="header-logo"><img src={this.state.logourl} alt=""/></div>
-        <Dropdown className="header-setting" overlay={
+        {/* <Dropdown className="header-setting" overlay={
           <Menu>
             <Menu.Item key="2" onClick={this.logout}>{this.state.dict['mob.logout']}</Menu.Item>
           </Menu>
-        }>
-          <div>
+        }> */}
+          <div className="header-setting">
             <img src={this.state.avatar} alt=""/>
             <span>
-              <span className="username">{this.state.userName}</span> <Icon type="down" />
+              <span className="username">{this.state.userName}</span>{/* <Icon type="down" /> */}
             </span>
           </div>
-        </Dropdown>
+        {/* </Dropdown> */}
       </header>
     )
   }
diff --git a/src/menu/menuform/index.jsx b/src/menu/menuform/index.jsx
index 16bbb7e..a08e50b 100644
--- a/src/menu/menuform/index.jsx
+++ b/src/menu/menuform/index.jsx
@@ -58,7 +58,7 @@
           }
         })
 
-        this.props.initMenuList(fromJS(menulist).toJS())
+        this.props.initMenuList({fstMenuList: fromJS(menulist).toJS(), fstMenuId: result.FstIDSeleted})
 
         this.setState({
           fstMenuId: result.FstIDSeleted,
@@ -105,8 +105,8 @@
       }, () => {
         let _id = smenulist[0] ? smenulist[0].value : ''
         this.props.form.setFieldsValue({parentId: _id})
+        this.props.updateConfig({...config, fstMenuId: value, parentId: _id})
       })
-      this.props.updateConfig({...config, fstMenuId: value})
     } else if (key === 'parentId') {
       this.props.updateConfig({...config, parentId: value})
     }
diff --git a/src/menu/menushell/card.jsx b/src/menu/menushell/card.jsx
index ac08014..85eb1bb 100644
--- a/src/menu/menushell/card.jsx
+++ b/src/menu/menushell/card.jsx
@@ -38,7 +38,7 @@
   }
 
   const getCardComponent = () => {
-    if (card.type === 'bar') {
+    if (card.type === 'bar' || card.type === 'line') {
       return (<AntvBar config={config} card={card} triggerEdit={editCard} editId={editId} updateConfig={updateConfig} />)
     }
   }
diff --git a/src/menu/modelsource/option.jsx b/src/menu/modelsource/option.jsx
index d5251e8..c796863 100644
--- a/src/menu/modelsource/option.jsx
+++ b/src/menu/modelsource/option.jsx
@@ -2,11 +2,15 @@
 // import enUS from '@/locales/en-US/mob.js'
 import bar from '@/assets/mobimg/bar.png'
 import bar1 from '@/assets/mobimg/bar1.png'
+import line from '@/assets/mobimg/line.png'
+import line1 from '@/assets/mobimg/line1.png'
 
 // const _dict =  sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 // 缁勪欢閰嶇疆淇℃伅
 export const menuOptions = [
+  { type: 'menu', url: line, component: 'line', subtype: 'line' },
+  { type: 'menu', url: line1, component: 'line', subtype: 'line1' },
   { type: 'menu', url: bar, component: 'bar', subtype: 'bar' },
   { type: 'menu', url: bar1, component: 'bar', subtype: 'bar1' },
 ]
diff --git a/src/tabviews/custom/index.jsx b/src/tabviews/custom/index.jsx
new file mode 100644
index 0000000..bf539b6
--- /dev/null
+++ b/src/tabviews/custom/index.jsx
@@ -0,0 +1,630 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import {connect} from 'react-redux'
+import { is, fromJS } from 'immutable'
+import { notification, Spin, Modal, Button, message, Tree, Typography } from 'antd'
+import moment from 'moment'
+
+import Api from '@/api'
+import options from '@/store/options.js'
+import zhCN from '@/locales/zh-CN/main.js'
+import enUS from '@/locales/en-US/main.js'
+import Utils from '@/utils/utils.js'
+import asyncSpinComponent from '@/utils/asyncSpinComponent'
+import { refreshTabView } from '@/store/action'
+
+import MainSearch from '@/tabviews/zshare/topSearch'
+import NotFount from '@/components/404'
+import './index.scss'
+
+// 閫氱敤缁勪欢
+const CalendarComponent = asyncSpinComponent(() => import('@/tabviews/zshare/calendar'))
+const SubTabTable = asyncSpinComponent(() => import('@/tabviews/subtabtable'))
+
+const { TreeNode } = Tree
+const { Paragraph } = Typography
+
+class NormalTable extends Component {
+  static propTpyes = {
+    param: PropTypes.any,        // 鍏朵粬椤甸潰浼犻�掔殑鍙傛暟
+    MenuID: PropTypes.string,    // 鑿滃崟Id
+    MenuNo: PropTypes.string,    // 鑿滃崟鍙傛暟
+    MenuName: PropTypes.string   // 鑿滃崟鍚嶇О
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    ContainerId: Utils.getuuid(), // 鑿滃崟澶栧眰html Id
+    BID: null,            // 椤甸潰璺宠浆鏃舵惡甯D
+    loadingview: true,    // 椤甸潰鍔犺浇涓�
+    viewlost: false,      // 椤甸潰涓㈠け锛�1銆佹湭鑾峰彇鍒伴厤缃�-椤甸潰涓㈠け锛�2銆侀〉闈㈡湭鍚敤
+    lostmsg: '',          // 椤甸潰涓㈠け鏃剁殑鎻愮ず淇℃伅
+    config: {},           // 椤甸潰閰嶇疆淇℃伅锛屽寘鎷寜閽�佹悳绱€�佹樉绀哄垪銆佹爣绛剧瓑
+    userConfig: null,     // 鐢ㄦ埛鑷畾涔夎缃�
+    searchlist: null,     // 鎼滅储鏉′欢
+    arr_field: '',        // 浣跨敤 sPC_Get_TableData 鏃剁殑鏌ヨ瀛楁闆�
+    setting: null,        // 椤甸潰鍏ㄥ眬璁剧疆锛氭暟鎹簮銆佹寜閽強鏄剧ず鍒楀浐瀹氥�佷富閿瓑
+    data: null,           // 鍒楄〃鏁版嵁闆�
+    loading: false,       // 鍒楄〃鏁版嵁鍔犺浇涓�
+    search: '',           // 鎼滅储鏉′欢鏁扮粍锛屼娇鐢ㄦ椂闇�鍒嗗満鏅鐞�
+    visible: false,       // 鏍囩椤垫帶鍒�
+    triggerTime: '',      // 鐐瑰嚮鏃堕棿
+    treevisible: false,   // 鑿滃崟缁撴瀯鏍戝脊妗嗘樉绀洪殣钘忔帶鍒�
+    calendarYear: moment().format('YYYY') // 鏃ュ巻骞翠唤
+  }
+
+  /**
+   * @description 鑾峰彇椤甸潰閰嶇疆淇℃伅
+   */
+  async loadconfig () {
+    const { permAction, param } = this.props
+
+    let _param = {
+      func: 'sPC_Get_LongParam',
+      MenuID: this.props.MenuID
+    }
+    let result = await Api.getCacheConfig(_param)
+
+    if (result.status) {
+      let config = ''
+      let userConfig = null
+      let _curUserConfig = ''
+
+      try { // 閰嶇疆淇℃伅瑙f瀽
+        config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
+      } catch (e) {
+        console.warn('Parse Failure')
+        config = ''
+      }
+      
+      // HS涓嶄娇鐢ㄨ嚜瀹氫箟璁剧疆
+      if (result.LongParamUser && this.props.menuType !== 'HS') {
+        try { // 閰嶇疆淇℃伅瑙f瀽
+          userConfig = JSON.parse(window.decodeURIComponent(window.atob(result.LongParamUser)))
+          _curUserConfig = userConfig[this.props.MenuID]
+        } catch (e) {
+          console.warn('Parse Failure')
+          userConfig = null
+        }
+      }
+
+      // 椤甸潰閰嶇疆瑙f瀽閿欒鏃舵彁绀�
+      if (!config) {
+        this.setState({
+          loadingview: false,
+          viewlost: true
+        })
+        return
+      }
+
+      // 椤甸潰鏈惎鐢ㄦ椂锛屾樉绀烘湭鍚敤椤甸潰
+      if (!config.enabled) {
+        this.setState({
+          loadingview: false,
+          viewlost: true,
+          lostmsg: this.state.dict['main.view.unenabled']
+        })
+        return
+      }
+
+      // 鏉冮檺杩囨护
+      if (this.props.menuType !== 'HS') {
+        if (config.tab && !permAction[config.tab.linkTab]) {
+          config.tab = null
+        }
+      }
+
+      if (_curUserConfig) {
+        config.setting = {...config.setting, ..._curUserConfig.setting}
+        config.easyCode = _curUserConfig.easyCode || config.easyCode || ''
+      }
+
+      // 瀛楁鏉冮檺榛戝悕鍗曘�佸繀濉�佸瓧娈甸�忚
+      let valid = true
+      config.search = config.search.map(item => {
+        item.oriInitval = item.initval
+        if (['text', 'select', 'link'].includes(item.type) && param) {
+          if (param.searchkey === item.field) {
+            item.initval = param.searchval
+          } else if (param.BID && item.field.toLowerCase() === 'bid') {
+            item.initval = param.BID
+          } else if (param.data && param.data[item.field]) {
+            item.initval = param.data[item.field]
+          }
+        }
+
+        if (item.required === 'true' && !item.initval) {
+          valid = false
+        }
+
+        if (!item.blacklist || item.blacklist.length === 0) return item
+
+        let _black = item.blacklist.filter(v => {
+          return this.props.permRoles.indexOf(v) !== -1
+        })
+
+        if (_black.length > 0) {
+          item.Hide = 'true'
+        }
+
+        return item
+      })
+
+      this.setState({
+        BID: param && param.BID ? param.BID : '',
+        loadingview: false,
+        config: config,
+        userConfig: userConfig,
+        setting: config.setting,
+        searchlist: config.search,
+        arr_field: config.columns.map(item => item.field).join(','),
+        search: Utils.initMainSearch(config.search) // 鎼滅储鏉′欢鍒濆鍖栵紙鍚湁鏃堕棿鏍煎紡锛岄渶瑕佽浆鍖栵級
+      }, () => {
+        if (config.setting.onload !== 'false' && valid) { // 鍒濆鍖栧彲鍔犺浇
+          this.loadmaindata()
+        }
+      })
+    } else {
+      this.setState({
+        loadingview: false,
+        viewlost: true
+      })
+      notification.warning({
+        top: 92,
+        message: result.message,
+        duration: 5
+      })
+    }
+  }
+
+  /**
+   * @description 涓昏〃鏁版嵁鍔犺浇
+   */ 
+  async loadmaindata () {
+    const { setting, search, BID } = this.state
+    let param = ''
+    let requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
+
+    if (requireFields.length > 0) {
+      let labels = requireFields.map(item => item.label)
+      labels = Array.from(new Set(labels))
+
+      notification.warning({
+        top: 92,
+        message: this.state.dict['form.required.input'] + labels.join('銆�') + ' !',
+        duration: 3
+      })
+      return
+    }
+
+    this.setState({
+      loading: true
+    })
+
+    if (setting.interType !== 'inner' || (setting.interType === 'inner' && setting.innerFunc)) {
+      param = this.getCustomParam()
+    } else {
+      param = this.getDefaultParam()
+    }
+
+    if (BID) {
+      param.BID = BID
+    }
+    // 鏁版嵁绠$悊鏉冮檺
+    if (this.props.dataManager) {
+      param.dataM = 'Y'
+    }
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      this.setState({
+        data: result.data.map((item, index) => {
+          item.key = index
+          return item
+        }),
+        loading: false
+      })
+    } else {
+      this.setState({
+        loading: false
+      })
+      notification.error({
+        top: 92,
+        message: result.message,
+        duration: 10
+      })
+    }
+  }
+
+  /**
+   * @description 鑾峰彇鐢ㄦ埛鑷畾涔夊瓨鍌ㄨ繃绋嬩紶鍙�
+   */
+  getCustomParam = () => {
+    const { search, setting, calendarYear, config } = this.state
+
+    let _search = Utils.formatCustomMainSearch(search)
+
+    let param = {
+      ..._search
+    }
+
+    if (config.calendar.refresh === 'true') {
+      param.calendarDate = calendarYear
+    }
+
+    if (setting.interType === 'inner') {
+      param.func = setting.innerFunc
+    } else {
+      if (this.props.menuType === 'HS') {
+        if (setting.sysInterface === 'true' && options.cloudServiceApi) {
+          param.rduri = options.cloudServiceApi
+        } else if (setting.sysInterface !== 'true') {
+          param.rduri = setting.interface
+        }
+      } else {
+        if (setting.sysInterface === 'true' && window.GLOB.mainSystemApi) {
+          param.rduri = window.GLOB.mainSystemApi
+        } else if (setting.sysInterface !== 'true') {
+          param.rduri = setting.interface
+        }
+      }
+
+      if (setting.outerFunc) {
+        param.func = setting.outerFunc
+      }
+    }
+
+    return param
+  }
+
+  /**
+   * @description 鑾峰彇绯荤粺瀛樺偍杩囩▼ sPC_Get_TableData 鐨勫弬鏁�
+   */
+  getDefaultParam = () => {
+    const { arr_field, search, setting, config, calendarYear } = this.state
+
+    let _search = Utils.joinMainSearchkey(search)
+
+    _search = _search ? 'where ' + _search : ''
+    
+    let param = {
+      func: 'sPC_Get_TableData',
+      obj_name: 'data',
+      arr_field: arr_field,
+      custom_script: setting.customScript || '',
+      default_sql: setting.default || 'true'
+    }
+    
+    let _dataresource = setting.dataresource
+
+    if (/\s/.test(_dataresource)) {
+      _dataresource = '(' + _dataresource + ') tb'
+    }
+
+    if (this.props.dataManager) { // 鏁版嵁鏉冮檺
+      _dataresource = _dataresource.replace(/\$@/ig, '/*')
+      _dataresource = _dataresource.replace(/@\$/ig, '*/')
+      param.custom_script = param.custom_script.replace(/\$@/ig, '/*')
+      param.custom_script = param.custom_script.replace(/@\$/ig, '*/')
+    } else {
+      _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
+      param.custom_script = param.custom_script.replace(/@\$|\$@/ig, '')
+    }
+
+    let regoptions = null
+    if (setting.queryType === 'statistics' || param.custom_script) {
+      let allSearch = Utils.getAllSearchOptions(search)
+
+      regoptions = allSearch.map(item => {
+        return {
+          reg: new RegExp('@' + item.key + '@', 'ig'),
+          value: `'${item.value}'`
+        }
+      })
+    }
+
+    if (config.calendar.refresh === 'true' && regoptions) {
+      regoptions.push({
+        reg: new RegExp('@calendarDate@', 'ig'),
+        value: `${calendarYear}-01-01 00:00:00.000`
+      })
+      regoptions.push({
+        reg: new RegExp('@calendarDate1@', 'ig'),
+        value: `${calendarYear}-12-31 23:59:59.999`
+      })
+    }
+
+    if (setting.queryType === 'statistics' && setting.default !== 'false') { // 缁熻鏁版嵁婧愶紝鍐呭鏇挎崲
+      regoptions.forEach(item => {
+        _dataresource = _dataresource.replace(item.reg, item.value)
+      })
+      _search = ''
+    }
+
+    let LText = ''
+
+    if (setting.default !== 'false') {
+      LText = `select ${arr_field} from ${_dataresource} ${_search}`
+    }
+
+    if (param.custom_script) {
+      regoptions.forEach(item => {
+        param.custom_script = param.custom_script.replace(item.reg, item.value)
+      })
+
+      if (LText) {
+        LText += `
+          aaa:
+          if @ErrorCode!=''
+            insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@ 
+        `
+      } else {
+        param.custom_script += `
+          aaa:
+          if @ErrorCode!=''
+            insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
+        `
+      }
+    }
+
+    // 娴嬭瘯绯荤粺鎵撳嵃鏌ヨ璇彞
+    if ((options.sysType === 'local' && !window.GLOB.systemType) || window.debugger === true) {
+      param.custom_script &&  console.log(`${LText ? '' : '/*涓嶆墽琛岄粯璁ql*/\n'}${param.custom_script}`)
+      LText &&  console.log(LText)
+    }
+    
+    param.custom_script = Utils.formatOptions(param.custom_script)
+    param.LText = Utils.formatOptions(LText)
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+    param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+    param.DateCount = ''
+
+    if (this.props.menuType === 'HS') { // 浜戠鏁版嵁楠岃瘉
+      param.open_key = Utils.encrypt(param.secretkey, param.timestamp, true)
+    }
+
+    return param
+  }
+
+  /**
+   * @description 鎼滅储鏉′欢鏀瑰彉鏃讹紝閲嶇疆琛ㄦ牸鏁版嵁
+   * 鍚湁鍒濆涓嶅姞杞界殑椤甸潰锛屼慨鏀硅缃�
+   */
+  refreshbysearch = (searches) => {
+    const { setting } = this.state
+
+    if (setting.onload === 'false') {
+      this.setState({
+        search: searches,
+        setting: {...setting, onload: 'true'}
+      }, () => {
+        this.loadmaindata()
+      })
+    } else {
+      this.setState({
+        search: searches
+      }, () => {
+        this.loadmaindata()
+      })
+    }
+  }
+
+
+  /**
+   * @description 椤甸潰鍒锋柊锛岄噸鏂拌幏鍙栭厤缃�
+   */
+  reloadview = () => {
+    this.setState({ loadingview: true, viewlost: false, lostmsg: '', data: null, loading: false, search: ''
+    }, () => {
+      this.loadconfig()
+    })
+  }
+
+  handleviewconfig = (e) => {
+    e.stopPropagation()
+
+    const { MenuNo } = this.props
+    const { config } = this.state
+
+    if (config && config.funcs && config.funcs.length > 0) {
+      this.setState({
+        treevisible: true
+      })
+    } else {
+      let oInput = document.createElement('input')
+      oInput.value = MenuNo || ''
+      document.body.appendChild(oInput)
+      oInput.select()
+      document.execCommand('Copy')
+      document.body.removeChild(oInput)
+      message.success(this.state.dict['main.copy.success'])
+    }
+  }
+
+  getTreeNode = (data) => {
+    let _type = {
+      view: '椤甸潰',
+      btn: '鎸夐挳',
+      tab: '鏍囩'
+    }
+
+    return data.map(item => {
+      let _title = _type[item.subtype]
+      let _others = []
+
+      _others.push(
+        (item.menuNo ? item.menuNo + '(鑿滃崟鍙傛暟)' : ''),
+        (item.tableName ? item.tableName + '(琛ㄥ悕) ' : ''),
+        (item.innerFunc ? item.innerFunc + '(鍐呴儴鍑芥暟) ' : ''),
+        (item.outerFunc ? item.outerFunc + '(澶栭儴鍑芥暟)' : '')
+      )
+      _others = _others.filter(Boolean)
+      _others = _others.join('銆�')
+
+      if (item.label) {
+        _title = _title + '(' + item.label + ')'
+      }
+      if (_others) {
+        _title = _title + ': ' + _others
+      }
+
+      if (item.subfuncs && item.subfuncs.length > 0) {
+        return (
+          <TreeNode title={_title} key={item.uuid} dataRef={item} selectable={false}>
+            {this.getTreeNode(item.subfuncs)}
+          </TreeNode>
+        )
+      }
+      return <TreeNode key={item.uuid} title={_title} isLeaf selectable={false} />
+    })
+  }
+
+  UNSAFE_componentWillMount () {
+    // 缁勪欢鍔犺浇鏃讹紝鑾峰彇鑿滃崟鏁版嵁
+    this.loadconfig()
+  }
+
+  UNSAFE_componentWillReceiveProps(nextProps) {
+    if (nextProps.refreshTab && nextProps.refreshTab.MenuID === this.props.MenuID) {
+      if (nextProps.refreshTab.position === 'grid') {
+        this.loadmaindata()
+      } else if (nextProps.refreshTab.position === 'view') {
+        this.reloadview()
+      }
+      this.props.refreshTabView('')
+    } else if (nextProps.param && !is(fromJS(this.props.param), fromJS(nextProps.param))) {
+      let search = this.state.search.map(item => {
+        if (item.type === 'text' && item.key === nextProps.param.searchkey) {
+          item.value = nextProps.param.searchval
+        }
+        return item
+      })
+      this.refreshbysearch(search)
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  changeDate = (value) => {
+    this.setState({calendarYear: value}, () => {
+      this.loadmaindata()
+    })
+  }
+
+  triggerDate = (item) => {
+    const { config } = this.state
+
+    if (!config.tab) return
+    
+    this.setState({
+      visible: true,
+      triggerTime: item.time.substr(0, 4) + '-' + item.time.substr(4, 2) + '-' + item.time.substr(6, 2)
+    })
+  }
+
+  closeTab = () => {
+    this.setState({
+      visible: false,
+      triggerTime: ''
+    })
+  }
+
+  render() {
+    const { BID, searchlist, loadingview, viewlost, config, loading, data, triggerTime } = this.state
+
+    return (
+      <div className="calendar-page" id={this.state.ContainerId}>
+        {loadingview && <Spin size="large" />}
+        {searchlist && searchlist.length > 0 ?
+          <MainSearch
+            BID={BID}
+            dict={this.state.dict}
+            searchlist={searchlist}
+            menuType={this.props.menuType}
+            dataManager={this.props.dataManager}
+            refreshdata={this.refreshbysearch}
+          /> : null
+        }
+        {config && config.calendar ? <CalendarComponent calendar={config.calendar} loading={loading} data={data} triggerDate={this.triggerDate} changeDate={this.changeDate}/> : null}
+        {options.sysType !== 'cloud' ? <Button
+          icon="copy"
+          shape="circle"
+          className="common-table-copy"
+          onClick={this.handleviewconfig}
+        /> : null}
+        <Modal
+          className="menu-tree-modal"
+          title={'鑿滃崟缁撴瀯鏍�'}
+          width={'650px'}
+          maskClosable={false}
+          visible={this.state.treevisible}
+          onCancel={() => this.setState({treevisible: false})}
+          footer={[
+            <Button key="close" onClick={() => this.setState({treevisible: false})}>{this.state.dict['main.close']}</Button>
+          ]}
+          destroyOnClose
+        >
+          <div className="menu-header">
+            <span>鑿滃崟鍚嶇О锛歿this.props.MenuName}</span>
+            <span>鑿滃崟鍙傛暟锛歿<Paragraph copyable>{this.props.MenuNo}</Paragraph>}</span>
+          </div>
+          {this.state.treevisible ? <Tree defaultExpandAll showLine={true}>
+            {this.getTreeNode(config.funcs)}
+          </Tree> : null}
+        </Modal>
+        <Modal
+          title={config.tab ? config.tab.label : ''}
+          width={'80vw'}
+          maskClosable={false}
+          visible={this.state.visible}
+          onCancel={this.closeTab}
+          footer={[
+            <Button key="close" onClick={this.closeTab}>{this.state.dict['main.close']}</Button>
+          ]}
+          destroyOnClose
+        >
+          {config.tab ? <SubTabTable
+            type="calendar"
+            BID={triggerTime}
+            Tab={config.tab}
+            SupMenuID={this.props.MenuID}
+            MenuID={config.tab.linkTab}
+            refreshSupView={() => this.loadmaindata()}
+            closeModalView={this.closeTab}
+          /> : null}
+        </Modal>
+        {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
+      </div>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    menuType: state.editLevel,
+    tabviews: state.tabviews,
+    refreshTab: state.refreshTab,
+    permAction: state.permAction,
+    permRoles: state.permRoles,
+    dataManager: state.dataManager
+  }
+}
+
+const mapDispatchToProps = (dispatch) => {
+  return {
+    refreshTabView: (refreshTab) => dispatch(refreshTabView(refreshTab))
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(NormalTable)
\ No newline at end of file
diff --git a/src/tabviews/custom/index.scss b/src/tabviews/custom/index.scss
new file mode 100644
index 0000000..b35d19b
--- /dev/null
+++ b/src/tabviews/custom/index.scss
@@ -0,0 +1,58 @@
+.calendar-page {
+  position: relative;
+  min-height: calc(100vh - 94px);
+  padding-top: 16px;
+  padding-bottom: 80px;
+  .box404 {
+    padding-top: 30px;
+  }
+
+  .ant-modal-mask {
+    position: absolute;
+  }
+  .ant-modal-wrap {
+    position: absolute;
+  }
+  .action-modal .ant-modal {
+    top: 40px;
+    max-width: 95%;
+    .ant-modal-body {
+      max-height: calc(100vh - 265px);
+    }
+  }
+  > .ant-spin {
+    position: absolute;
+    z-index: 10;
+    left: calc(50% - 22px);
+    top: calc(50vh - 70px);
+  }
+  
+  .common-table-copy {
+    position: fixed;
+    z-index: 2;
+    bottom: 65px;
+    right: 30px;
+    width: 40px;
+    height: 40px;
+  }
+}
+
+.menu-tree-modal {
+  .ant-modal-body {
+    min-height: 300px;
+    .menu-header {
+      text-align: center;
+      span {
+        font-weight: 600;
+        margin-right: 20px;
+      }
+      .ant-typography {
+        font-weight: 600;
+        display: inline-block;
+      }
+    }
+    .ant-tree li .ant-tree-node-content-wrapper {
+      cursor: default;
+    }
+  }
+}
diff --git a/src/tabviews/zshare/cardcomponent/index.scss b/src/tabviews/zshare/cardcomponent/index.scss
index 0ae1b0b..e0e90ae 100644
--- a/src/tabviews/zshare/cardcomponent/index.scss
+++ b/src/tabviews/zshare/cardcomponent/index.scss
@@ -23,16 +23,6 @@
     color: #40a9ff;
   }
 
-  .chart-card-box:hover {
-    .ant-card.chart-card {
-      .ant-card-head {
-        .ant-card-extra {
-          opacity: 1;
-        }
-      }
-    }
-  }
-
   .chart-card-box:not(.ant-col) {
     display: inline-block;
     margin-bottom: 20px;
@@ -130,8 +120,6 @@
       }
       .ant-card-extra {
         padding: 0px;
-        opacity: 0;
-        transition: opacity 0.3s;
       }
     }
     .ant-card-actions {
diff --git a/src/utils/option.js b/src/utils/option.js
index f4e0ec0..7804842 100644
--- a/src/utils/option.js
+++ b/src/utils/option.js
@@ -4,7 +4,7 @@
 import mainsubtable from '@/assets/img/mainsubtable.jpg'
 import treepage from '@/assets/img/treepage.jpg'
 import calendar from '@/assets/img/calendar.jpg'
-// import customImg from '@/assets/img/custom.jpg'
+import customImg from '@/assets/img/custom.jpg'
 import rolemanage from '@/assets/img/rolemanage.jpg'
 
 const _dict =  sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
@@ -78,13 +78,13 @@
     baseconfig: '',
     isSystem: true
   },
-  // {
-  //   title: '鑷畾涔�',
-  //   type: 'CustomPage',
-  //   url: customImg,
-  //   baseconfig: '',
-  //   isSystem: true
-  // },
+  {
+    title: '鑷畾涔�',
+    type: 'CustomPage',
+    url: customImg,
+    baseconfig: '',
+    isSystem: true
+  },
   {
     title: '瑙掕壊鏉冮檺鍒嗛厤',
     type: 'RolePermission',
diff --git a/src/views/menudesign/index.jsx b/src/views/menudesign/index.jsx
index b7569eb..f7c06e1 100644
--- a/src/views/menudesign/index.jsx
+++ b/src/views/menudesign/index.jsx
@@ -26,14 +26,16 @@
 // const DataSource = asyncComponent(() => import('@/mob/datasource'))
 const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
 
+sessionStorage.setItem('isEditState', 'true')
+
 class Mobile extends Component {
   state = {
     dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     MenuId: this.props.match.params.MenuId,
     tableFields: [],
     activeKey: 'basedata',
+    menuloading: false,
     oriConfig: null,
-    parentId: '',
     openEdition: '',
     config: null,
     editElem: null
@@ -79,7 +81,20 @@
   }
 
   submitConfig = () => {
-    const { config, openEdition, parentId } = this.state
+    const { config, openEdition } = this.state
+
+    if (!config.MenuName || !config.MenuNo || !config.fstMenuId || !config.parentId) {
+      notification.warning({
+        top: 92,
+        message: '璇峰畬鍠勮彍鍗曞熀鏈俊鎭紒',
+        duration: 5
+      })
+      return
+    }
+
+    if (config.enabled && this.verifyConfig()) {
+      config.enabled = false
+    }
 
     let _config = fromJS(config).toJS()
     delete _config.fstMenuList
@@ -87,28 +102,36 @@
     delete _config.sysRoles
     delete _config.tableFields
 
+    let funcs = []
+    _config.components.forEach(component => {
+      if (component.setting && component.setting.innerFunc) {
+        funcs.push(`select '${_config.uuid}' as MenuID,'${component.setting.innerFunc}' as ProcName,'${component.setting.name}' as MenuName`)
+      }
+      if (component.action) {
+        component.action.forEach(item => {
+          if (!item.innerFunc) return
+          funcs.push(`select '${_config.uuid}' as MenuID,'${item.innerFunc}' as ProcName,'${item.label}' as MenuName`)
+        })
+      }
+    })
+
     let param = {
       func: 'sPC_TrdMenu_AddUpt',
-      ParentID: parentId,
-      MenuID: config.uuid,
-      MenuNo: config.MenuNo,
-      EasyCode: '',
-      Template: '',
-      MenuName: '',
-      PageParam: '',
+      FstID: _config.fstMenuId,
+      SndID: _config.parentId,
+      ParentID: _config.parentId,
+      MenuID: _config.uuid,
+      MenuNo: _config.MenuNo,
+      EasyCode: _config.easyCode,
+      Template: 'CustomPage',
+      MenuName: _config.MenuName,
+      PageParam: JSON.stringify({Template: 'CustomPage', OpenType: 'newtab'}),
       LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_config))),
-      // LText: _vals.func.map(item => `select '${menu.MenuID}' as MenuID,'${item.func}' as ProcName,'${item.label}' as MenuName`),
-      // LTexttb: _tables.map(item => `select '${menu.MenuID}' as MenuID,'${item}' as tbName`),
-      TypeCharOne: 'mob'
+      LText: funcs.join(' union all '),
+      LTexttb: '', // 琛ㄥ悕
     }
 
-    let _LText = ''
-    // _LText = _LText.join(' union all ')
-    let _LTexttb = ''
-    // _LTexttb = _LTexttb.join(' union all ')
-    
-    param.LText = Utils.formatOptions(_LText)
-    param.LTexttb = Utils.formatOptions(_LTexttb)
+    param.LText = Utils.formatOptions(param.LText)
     param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
     param.secretkey = Utils.encrypt(param.LText, param.timestamp)
 
@@ -116,13 +139,27 @@
       param.open_edition = openEdition
     }
 
+    this.setState({
+      menuloading: true
+    })
+
     Api.getSystemConfig(param).then(response => {
       if (response.status) {
         this.setState({
           oriConfig: fromJS(config).toJS(),
-          openEdition: response.open_edition || ''
+          openEdition: response.open_edition || '',
+          menuloading: false
+        })
+        notification.success({
+          top: 92,
+          message: '淇濆瓨鎴愬姛',
+          duration: 2
         })
       } else {
+        this.setState({
+          openEdition: response.open_edition || '',
+          menuloading: false
+        })
         notification.warning({
           top: 92,
           message: response.message,
@@ -154,6 +191,7 @@
             version: 1.0,
             uuid: this.props.match.params.MenuId,
             MenuID: this.props.match.params.MenuId,
+            parentId: this.props.match.params.ParentId,
             Template: 'CustomPage',
             easyCode: '',
             enabled: false,
@@ -162,7 +200,11 @@
             tables: [],
             components: []
           }
+        } else {
+          config.uuid = this.props.match.params.MenuId
+          config.MenuID = this.props.match.params.MenuId
         }
+
         this.setState({
           oriConfig: config,
           config: fromJS(config).toJS(),
@@ -212,9 +254,9 @@
     })
   }
 
-  initMenuList = (list) => {
+  initMenuList = (msg) => {
     this.setState({
-      config: {...this.state.config, fstMenuList: list}
+      config: {...this.state.config, ...msg}
     })
   }
 
@@ -247,9 +289,43 @@
   onEnabledChange = () => {
     const { config } = this.state
 
+    if (!config.enabled && this.verifyConfig(true)) {
+      return
+    }
+
     this.setState({
       config: {...config, enabled: !config.enabled}
     })
+  }
+
+  verifyConfig = (show) => {
+    const { config } = this.state
+    let error = ''
+
+    if (!config.MenuName || !config.MenuNo || !config.fstMenuId || !config.parentId) {
+      notification.warning({
+        top: 92,
+        message: '璇峰畬鍠勮彍鍗曞熀鏈俊鎭紒',
+        duration: 5
+      })
+      return 'false'
+    }
+
+    config.components.forEach(item => {
+      if (!error && (!item.setting || !item.setting.dataresource)) {
+        error = `缁勪欢銆�${item.setting.name}銆嬫湭璁剧疆鏁版嵁婧恅
+      }
+    })
+
+    if (show && error) {
+      notification.warning({
+        top: 92,
+        message: error,
+        duration: 5
+      })
+    }
+
+    return error
   }
 
   // 鏇存柊閰嶇疆淇℃伅
@@ -322,7 +398,7 @@
                   <Button type="primary" onClick={this.submitConfig} loading={this.state.menuloading}>{dict['mob.save']}</Button>
                 </div>
               } style={{ width: '100%' }}>
-                {config ? <MenuShell config={config} handleList={this.updateConfig} deleteCard={this.deleteCard} /> : null}
+                {config && config.components ? <MenuShell config={config} handleList={this.updateConfig} deleteCard={this.deleteCard} /> : null}
               </Card>
             </div>
           </div>

--
Gitblit v1.8.0