From 5a7e0659e6365709e2f5c02307d5d5b0a6bd1cc8 Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期一, 22 二月 2021 17:15:40 +0800
Subject: [PATCH] 2021-02-22

---
 src/pc/padcontroller/index.scss                                                |    9 
 src/mob/header/index.jsx                                                       |    6 
 src/views/appmanage/index.scss                                                 |    0 
 src/views/design/header/index.jsx                                              |    6 
 src/pc/bgcontroller/index.jsx                                                  |  103 +++
 src/views/pcdesign/menuform/index.scss                                         |   10 
 src/menu/components/card/cardcellcomponent/index.jsx                           |    3 
 src/mob/datasource/verifycard/settingform/index.jsx                            |    2 
 src/views/appmanage/mutilform/index.scss                                       |    0 
 src/views/pcdesign/index.jsx                                                   |  989 ++++++++++++++++++++++++++++++++++
 src/views/pcdesign/menuform/index.jsx                                          |  161 +++++
 src/pc/padcontroller/index.jsx                                                 |  105 +++
 src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx |    2 
 src/menu/datasource/verifycard/settingform/index.jsx                           |    2 
 src/router/index.js                                                            |    8 
 src/pc/bgcontroller/index.scss                                                 |   43 +
 src/api/index.js                                                               |   14 
 src/views/pcdesign/index.scss                                                  |  190 ++++++
 src/views/appmanage/index.jsx                                                  |   12 
 src/views/appmanage/submutilform/index.scss                                    |    0 
 src/tabviews/zshare/settingcomponent/editTable/index.jsx                       |    2 
 src/views/appmanage/submutilform/index.jsx                                     |   12 
 src/views/mobdesign/index.jsx                                                  |   22 
 src/views/appmanage/mutilform/index.jsx                                        |    2 
 24 files changed, 1,646 insertions(+), 57 deletions(-)

diff --git a/src/api/index.js b/src/api/index.js
index aa7e885..64de6d8 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -373,7 +373,7 @@
    * @description 鑾峰彇鎴栦慨鏀逛簯绔厤缃�
    */
   getCloudConfig (param) {
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = param.lang || localStorage.getItem('lang') || ''
     param.appkey = window.GLOB.appkey || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
 
@@ -403,7 +403,7 @@
    * @description 鑾峰彇浜戠閰嶇疆锛屽苟缂撳瓨淇℃伅
    */
   getCloudCacheConfig (param) {
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = param.lang || localStorage.getItem('lang') || ''
     param.appkey = window.GLOB.appkey || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
 
@@ -452,7 +452,7 @@
    */
   getSystemConfig (param) {
     param.userid = sessionStorage.getItem('UserID') || ''
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = param.lang || localStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
     param.LoginUID = sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
@@ -479,7 +479,7 @@
    */
   getLocalConfig (param) {
     param.userid = sessionStorage.getItem('UserID') || ''
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = param.lang || localStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
     param.LoginUID = sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
@@ -499,7 +499,7 @@
    */
   getCacheConfig (param) {
     param.userid = sessionStorage.getItem('UserID') || ''
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = param.lang || localStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
     param.LoginUID = sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
@@ -593,7 +593,7 @@
    */
   getLocalCacheConfig (param) {
     param.userid = sessionStorage.getItem('UserID') || ''
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = param.lang || localStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
     param.LoginUID = sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
@@ -650,7 +650,7 @@
    */
   getSystemCacheConfig (param) {
     param.userid = param.userid || sessionStorage.getItem('UserID') || ''
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = param.lang || localStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
     param.LoginUID = param.LoginUID || sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
diff --git a/src/menu/components/card/cardcellcomponent/index.jsx b/src/menu/components/card/cardcellcomponent/index.jsx
index 045c3e1..24a3b29 100644
--- a/src/menu/components/card/cardcellcomponent/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/index.jsx
@@ -558,6 +558,9 @@
 
   dropButton = (id) => {
     const { cards } = this.props
+
+    if (!cards.action) return
+
     let index = cards.action.findIndex(item => item.uuid === id)
 
     if (index === -1) return
diff --git a/src/menu/datasource/verifycard/settingform/index.jsx b/src/menu/datasource/verifycard/settingform/index.jsx
index da631aa..a000237 100644
--- a/src/menu/datasource/verifycard/settingform/index.jsx
+++ b/src/menu/datasource/verifycard/settingform/index.jsx
@@ -315,7 +315,7 @@
             {config.format === 'array' ? <Col span={8}>
               <Form.Item label="榛樿鎺掑簭">
                 {getFieldDecorator('order', {
-                  initialValue: setting.order || '',
+                  initialValue: setting.order || 'ID asc',
                   rules: [
                     {
                       required: true,
diff --git a/src/mob/datasource/verifycard/settingform/index.jsx b/src/mob/datasource/verifycard/settingform/index.jsx
index cfe861a..41c3e3f 100644
--- a/src/mob/datasource/verifycard/settingform/index.jsx
+++ b/src/mob/datasource/verifycard/settingform/index.jsx
@@ -286,7 +286,7 @@
             {structure === 'array' ? <Col span={8}>
               <Form.Item label="榛樿鎺掑簭">
                 {getFieldDecorator('order', {
-                  initialValue: setting.order || ''
+                  initialValue: setting.order || 'ID asc'
                 })(<Input placeholder={'ID asc, UID desc'} autoComplete="off" />)}
               </Form.Item>
             </Col> : null}
diff --git a/src/mob/header/index.jsx b/src/mob/header/index.jsx
index 34db685..d2fa1e6 100644
--- a/src/mob/header/index.jsx
+++ b/src/mob/header/index.jsx
@@ -19,7 +19,6 @@
     view: PropTypes.string,
     saveIng: PropTypes.any,
     triggerSave: PropTypes.func,
-    jumpToManage: PropTypes.func
   }
 
   state = {
@@ -64,11 +63,6 @@
             theme="dark"
             inlineCollapsed={this.state.collapsed}
           >
-            <Menu.Item key="1">
-              <Tooltip placement="bottom" title="杩斿洖搴旂敤绠$悊">
-                <Icon type="arrow-left" onClick={this.props.jumpToManage} />
-              </Tooltip>
-            </Menu.Item>
             <Menu.Item key="2">
               <Tooltip placement="bottom" title="淇濆瓨">
                 <Button icon="save" loading={this.props.saveIng} onClick={this.props.triggerSave}></Button>
diff --git a/src/pc/bgcontroller/index.jsx b/src/pc/bgcontroller/index.jsx
new file mode 100644
index 0000000..ab2155c
--- /dev/null
+++ b/src/pc/bgcontroller/index.jsx
@@ -0,0 +1,103 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Form } from 'antd'
+
+import zhCN from '@/locales/zh-CN/mob.js'
+import enUS from '@/locales/en-US/mob.js'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
+const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
+
+class MobController extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    backgroundColor: '',
+    backgroundImage: '',
+  }
+
+  UNSAFE_componentWillMount () {
+    const { config } = this.props
+
+    let bgImg = config.style.backgroundImage || ''
+
+    if (bgImg && /^url/.test(bgImg)) {
+      bgImg = bgImg.replace('url(', '')
+      bgImg = bgImg.replace(')', '')
+    }
+
+    this.setState({
+      backgroundColor: config.style.backgroundColor,
+      backgroundImage: bgImg
+    })
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 淇敼鑳屾櫙棰滆壊 锛岄鑹叉帶浠�
+   */
+  changeBackgroundColor = (val) => {
+    let config = fromJS(this.props.config).toJS()
+
+    this.setState({
+      backgroundColor: val
+    })
+
+    config.style.backgroundColor = val
+    this.props.updateConfig(config)
+  }
+
+  imgChange = (val) => {
+    this.setState({
+      backgroundImage: val
+    })
+
+    let config = fromJS(this.props.config).toJS()
+
+    if (val) {
+      config.style.backgroundImage = `url(${val})`
+    } else {
+      delete config.style.backgroundImage
+    }
+    this.props.updateConfig(config)
+  }
+
+  render () {
+    const { backgroundColor, backgroundImage } = this.state
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 4 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 20 }
+      }
+    }
+
+    return (
+      <div className="menu-background-controller">
+        <Form {...formItemLayout}>
+          <Form.Item className="color-control" colon={false} label="棰滆壊">
+            <ColorSketch value={backgroundColor} onChange={this.changeBackgroundColor} />
+          </Form.Item>
+          <Form.Item colon={false} label="鍥剧墖">
+            <SourceComponent value={backgroundImage} type="" placement="right" onChange={this.imgChange}/>
+          </Form.Item>
+        </Form>
+      </div>
+    )
+  }
+}
+
+export default MobController
\ No newline at end of file
diff --git a/src/pc/bgcontroller/index.scss b/src/pc/bgcontroller/index.scss
new file mode 100644
index 0000000..0d6ed73
--- /dev/null
+++ b/src/pc/bgcontroller/index.scss
@@ -0,0 +1,43 @@
+.menu-background-controller {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  .color-control .ant-form-item-control {
+    padding-top: 10px;
+    line-height: 35px;
+  }
+  .mk-source-wrap {
+    height: 32px;
+    .mk-source-item-info {
+      top: 5px;
+    }
+  }
+}
+
+.margin-popover {
+  padding-top: 0px;
+  .ant-popover-inner-content {
+    width: 90px;
+    padding: 0px 5px;
+    .ant-menu-root.ant-menu-vertical {
+      border: 0;
+      .ant-menu-item {
+        height: 30px;
+        cursor: pointer;
+        line-height: 30px;
+      }
+      .ant-menu-item:not(:last-child) {
+        margin-bottom: 0px;
+      }
+      .ant-menu-item:first-child {
+        margin-top: 10px;
+      }
+      .ant-menu-item:last-child {
+        margin-bottom: 10px;
+      }
+    }
+  }
+  .ant-popover-arrow {
+    display: none;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/padcontroller/index.jsx b/src/pc/padcontroller/index.jsx
new file mode 100644
index 0000000..ec3f6bb
--- /dev/null
+++ b/src/pc/padcontroller/index.jsx
@@ -0,0 +1,105 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Form, Col, Icon } from 'antd'
+
+import zhCN from '@/locales/zh-CN/mob.js'
+import enUS from '@/locales/en-US/mob.js'
+import StyleInput from '@/menu/stylecontroller/styleInput'
+import './index.scss'
+
+class MobController extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    paddingTop: '',
+    paddingBottom: '',
+    paddingLeft: '',
+    paddingRight: ''
+  }
+
+  UNSAFE_componentWillMount () {
+
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 淇敼鑳屾櫙棰滆壊 锛岄鑹叉帶浠�
+   */
+  changePadding = (val, type) => {
+    let config = fromJS(this.props.config).toJS()
+
+    config.style[type] = val
+    this.props.updateConfig(config)
+  }
+
+  render () {
+    const { config } = this.props
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 4 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 20 }
+      }
+    }
+
+    return (
+      <div className="menu-padding-controller">
+        <Form {...formItemLayout}>
+          <Col span={24}>
+            <Form.Item
+              colon={false}
+              label={<Icon title="瀹藉害" type="column-width" />}
+            >
+              <StyleInput defaultValue={config.style.width || '100%'} options={['px', '%', 'vw']} onChange={(val) => this.changePadding(val, 'width')}/>
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              colon={false}
+              label={<Icon title="涓婅竟璺�" type="arrow-up"/>}
+            >
+              <StyleInput defaultValue={config.style.paddingTop || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingTop')}/>
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              colon={false}
+              label={<Icon title="涓嬭竟璺�" type="arrow-down"/>}
+            >
+              <StyleInput defaultValue={config.style.paddingBottom || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingBottom')}/>
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              colon={false}
+              label={<Icon title="宸﹁竟璺�" type="arrow-left"/>}
+            >
+              <StyleInput defaultValue={config.style.paddingLeft || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingLeft')}/>
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              colon={false}
+              label={<Icon title="鍙宠竟璺�" type="arrow-right"/>}
+            >
+              <StyleInput defaultValue={config.style.paddingRight || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingRight')}/>
+            </Form.Item>
+          </Col>
+        </Form>
+      </div>
+    )
+  }
+}
+
+export default MobController
\ No newline at end of file
diff --git a/src/pc/padcontroller/index.scss b/src/pc/padcontroller/index.scss
new file mode 100644
index 0000000..95c0abe
--- /dev/null
+++ b/src/pc/padcontroller/index.scss
@@ -0,0 +1,9 @@
+.menu-padding-controller {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  .ant-form-item label > .anticon {
+    font-size: 16px;
+    vertical-align: middle;
+  }
+}
diff --git a/src/router/index.js b/src/router/index.js
index 65fd766..8f61de0 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -12,7 +12,8 @@
 const Design = asyncLoadComponent(() => import('@/views/design'))
 const Login = asyncLoadComponent(() => import('@/views/login'))
 const NotFound = asyncComponent(() => import('@/views/404'))
-const MobManage = asyncLoadComponent(() => import('@/views/mobmanage'))
+const AppManage = asyncLoadComponent(() => import('@/views/appmanage'))
+const PCDesign = asyncLoadComponent(() => import('@/views/pcdesign'))
 const MobDesign = asyncLoadComponent(() => import('@/views/mobdesign'))
 const MenuDesign = asyncLoadComponent(() => import('@/views/menudesign'))
 const BillPrint = asyncLoadComponent(() => import('@/views/billprint'))
@@ -25,8 +26,9 @@
   {path: '/ssologin/:param', name: 'ssologin', component: Sso, auth: false},
   {path: '/main', name: 'main', component: Main, auth: true},
   {path: '/design', name: 'design', component: Design, auth: true},
-  {path: '/mobmanage', name: 'mobmanage', component: MobManage, auth: true},
-  {path: '/mobdesign/:appId/:appType/:appCode/:appName', name: 'mobdesign', component: MobDesign, auth: true},
+  {path: '/appmanage', name: 'appmanage', component: AppManage, auth: true},
+  {path: '/pcdesign/:param', name: 'pcdesign', component: PCDesign, auth: true},
+  {path: '/mobdesign/:param', name: 'mobdesign', component: MobDesign, auth: true},
   {path: '/menudesign/:param', name: 'menudesign', component: MenuDesign, auth: true},
   {path: '/billprint/:param', name: 'billprint', component: BillPrint, auth: true},
   {path: '/paramsmain/:param', name: 'pmain', component: Main, auth: true}
diff --git a/src/tabviews/zshare/settingcomponent/editTable/index.jsx b/src/tabviews/zshare/settingcomponent/editTable/index.jsx
index 214f0ad..7f8286f 100644
--- a/src/tabviews/zshare/settingcomponent/editTable/index.jsx
+++ b/src/tabviews/zshare/settingcomponent/editTable/index.jsx
@@ -21,7 +21,7 @@
     if (inputType === 'select') {
       let _options = []
       if (record.$port) {
-        _options = window.GLOB.UserCacheMap.get(record.$port)
+        _options = window.GLOB.UserCacheMap.get(record.$port) || []
       }
       return (
         <Select allowClear>
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
index e303c63..7c11299 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
@@ -465,7 +465,7 @@
             <Col span={12}>
               <Form.Item label="榛樿鎺掑簭">
                 {getFieldDecorator('order', {
-                  initialValue: setting.order || '',
+                  initialValue: setting.order || 'ID asc',
                   rules: [
                     {
                       required: true,
diff --git a/src/views/mobmanage/index.jsx b/src/views/appmanage/index.jsx
similarity index 97%
rename from src/views/mobmanage/index.jsx
rename to src/views/appmanage/index.jsx
index 64056b2..8b9b2fc 100644
--- a/src/views/mobmanage/index.jsx
+++ b/src/views/appmanage/index.jsx
@@ -231,7 +231,14 @@
   }
   
   jumpApp = (item) => {
-    console.log(item)
+    const { selectApp } = this.state
+
+    let route = 'mobdesign'
+    if (item.typename === 'pc') {
+      route = 'pcdesign'
+    }
+
+    window.open(window.location.href.replace(/#.+/ig, `#/${route}/${window.btoa(window.encodeURIComponent(JSON.stringify({...item, kei_no: selectApp.kei_no, remark: selectApp.remark, type: 'app'})))}`))
   }
 
   /**
@@ -262,7 +269,6 @@
    * @description 鐐瑰嚮鏁磋锛岃Е鍙戝垏鎹紝 鍒ゆ柇鏄惁鍙�夛紝鍗曢�夋垨澶氶�夛紝杩涜瀵瑰簲鎿嶄綔
    */
   changeSubRow = (record) => {
-    console.log(record)
     this.setState({ selectedSubRowKeys: [record.ID], selectSubApp: record })
   }
 
@@ -370,7 +376,7 @@
     const { selectApp, subVisible, selectSubApp } = this.state
 
     this.submobcardRef.handleConfirm().then(res => {
-      if (subVisible === 'plus' && selectApp.sublist.filter(item => item.typename === res.typename).length > 0) {
+      if (subVisible === 'plus' && selectApp.sublist.filter(item => item.typename === res.typename && item.lang === res.lang).length > 0) {
         notification.warning({
           top: 92,
           message: '搴旂敤绫诲瀷宸插瓨鍦紒',
diff --git a/src/views/mobmanage/index.scss b/src/views/appmanage/index.scss
similarity index 100%
rename from src/views/mobmanage/index.scss
rename to src/views/appmanage/index.scss
diff --git a/src/views/mobmanage/mutilform/index.jsx b/src/views/appmanage/mutilform/index.jsx
similarity index 98%
rename from src/views/mobmanage/mutilform/index.jsx
rename to src/views/appmanage/mutilform/index.jsx
index 0dca07e..f27151f 100644
--- a/src/views/mobmanage/mutilform/index.jsx
+++ b/src/views/appmanage/mutilform/index.jsx
@@ -43,7 +43,7 @@
       },
       wrapperCol: {
         xs: { span: 24 },
-        sm: { span: 16 }
+        sm: { span: 12 }
       }
     }
     return (
diff --git a/src/views/mobmanage/mutilform/index.scss b/src/views/appmanage/mutilform/index.scss
similarity index 100%
rename from src/views/mobmanage/mutilform/index.scss
rename to src/views/appmanage/mutilform/index.scss
diff --git a/src/views/mobmanage/submutilform/index.jsx b/src/views/appmanage/submutilform/index.jsx
similarity index 91%
rename from src/views/mobmanage/submutilform/index.jsx
rename to src/views/appmanage/submutilform/index.jsx
index 36af92e..10741c4 100644
--- a/src/views/mobmanage/submutilform/index.jsx
+++ b/src/views/appmanage/submutilform/index.jsx
@@ -43,7 +43,7 @@
       },
       wrapperCol: {
         xs: { span: 24 },
-        sm: { span: 16 }
+        sm: { span: 12 }
       }
     }
     return (
@@ -52,13 +52,7 @@
           <Col span={24}>
             <Form.Item label="搴旂敤绫诲瀷">
               {getFieldDecorator('typename', {
-                initialValue: card ? card.typename : 'mob',
-                rules: [
-                  {
-                    required: true,
-                    message: '璇烽�夋嫨搴旂敤绫诲瀷!'
-                  }
-                ]
+                initialValue: card ? card.typename : 'mob'
               })(
                 <Select disabled={type === 'edit'}>
                   <Select.Option value="mob">绉诲姩绔�(鍖呮嫭android銆乮os)</Select.Option>
@@ -72,7 +66,7 @@
               {getFieldDecorator('lang', {
                 initialValue: card ? card.lang || 'zh-CN' : 'zh-CN'
               })(
-                <Radio.Group>
+                <Radio.Group disabled={type === 'edit'}>
                   <Radio value="zh-CN">涓枃</Radio>
                   <Radio value="en-US">鑻辨枃</Radio>
                 </Radio.Group>
diff --git a/src/views/mobmanage/submutilform/index.scss b/src/views/appmanage/submutilform/index.scss
similarity index 100%
rename from src/views/mobmanage/submutilform/index.scss
rename to src/views/appmanage/submutilform/index.scss
diff --git a/src/views/design/header/index.jsx b/src/views/design/header/index.jsx
index 99584b2..97057a3 100644
--- a/src/views/design/header/index.jsx
+++ b/src/views/design/header/index.jsx
@@ -328,9 +328,9 @@
         {editLevel === 'HS' ? <Button className="level4-close" type="primary" onClick={this.exitManage}>閫�鍑�</Button> : null}
         {/* 杩涘叆缂栬緫鎸夐挳 */}
         {!editLevel ? <Icon onClick={this.enterEdit} className="edit-check" type="edit" /> : null}
-        {/* {!editLevel && options.sysType === 'local' && window.GLOB.systemType !== 'production' ?
-          <a href="#/mobmanage" target="_blank" className="mobile" type="edit"> 搴旂敤绠$悊 <Icon type="arrow-right" /></a> : null
-        } */}
+        {!editLevel && options.sysType === 'local' && window.GLOB.systemType !== 'production' ?
+          <a href="#/appmanage" target="_blank" className="mobile" type="edit"> 搴旂敤绠$悊 <Icon type="arrow-right" /></a> : null
+        }
         {/* window.btoa(window.encodeURIComponent(JSON.stringify({ MenuType: 'home', MenuId: 'home_page_id', MenuName: '棣栭〉' }))) */}
         {!editLevel && window.GLOB.systemType !== 'production' && this.props.memberLevel >= 20 ?
           <a className="home-edit" href={`#/menudesign/JTdCJTIyTWVudVR5cGUlMjIlM0ElMjJob21lJTIyJTJDJTIyTWVudUlkJTIyJTNBJTIyaG9tZV9wYWdlX2lkJTIyJTJDJTIyTWVudU5hbWUlMjIlM0ElMjIlRTklQTYlOTYlRTklQTElQjUlMjIlN0Q=`} target="_blank" rel="noopener noreferrer">
diff --git a/src/views/mobdesign/index.jsx b/src/views/mobdesign/index.jsx
index b090a70..552d5e1 100644
--- a/src/views/mobdesign/index.jsx
+++ b/src/views/mobdesign/index.jsx
@@ -1,7 +1,7 @@
 import React, { Component } from 'react'
 import { connect } from 'react-redux'
 import { DndProvider } from 'react-dnd'
-import { is, fromJS } from 'immutable'
+import { fromJS } from 'immutable'
 import moment from 'moment'
 import HTML5Backend from 'react-dnd-html5-backend'
 import { Icon, Tabs, notification, Modal } from 'antd'
@@ -50,26 +50,6 @@
   componentWillUnmount () {
     this.setState = () => {
       return
-    }
-  }
-
-  jumpToManage = () => {
-    const { oriConfig, config } = this.state
-    const _this = this
-
-    if (!is(fromJS(oriConfig), fromJS(config))) {
-      confirm({
-        title: '閰嶇疆宸蹭慨鏀癸紝鏀惧純淇濆瓨鍚楋紵',
-        content: '',
-        okText: _this.state.dict['mob.confirm'],
-        cancelText: _this.state.dict['mob.cancel'],
-        onOk() {
-          _this.props.history.replace('/mobmanage')
-        },
-        onCancel() {}
-      })
-    } else {
-      _this.props.history.replace('/mobmanage')
     }
   }
 
diff --git a/src/views/pcdesign/index.jsx b/src/views/pcdesign/index.jsx
new file mode 100644
index 0000000..1c39a05
--- /dev/null
+++ b/src/views/pcdesign/index.jsx
@@ -0,0 +1,989 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import { DndProvider } from 'react-dnd'
+import { is, fromJS } from 'immutable'
+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'
+import antdEnUS from 'antd/es/locale/en_US'
+import antdZhCN from 'antd/es/locale/zh_CN'
+import MKEmitter from '@/utils/events.js'
+import MenuUtils from '@/menu/utils/menuUtils.js'
+import asyncComponent from '@/utils/asyncComponent'
+import { modifyCustomMenu } from '@/store/action'
+
+import './index.scss'
+
+const { Panel } = Collapse
+const { confirm } = Modal
+const _locale = localStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS
+
+const MenuForm = asyncComponent(() => import('./menuform'))
+const MenuShell = asyncComponent(() => import('@/menu/menushell'))
+const SourceWrap = asyncComponent(() => import('@/menu/modulesource'))
+const BgController = asyncComponent(() => import('@/pc/bgcontroller'))
+const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
+const PaddingController = asyncComponent(() => import('@/pc/padcontroller'))
+const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
+const SysInterface = asyncComponent(() => import('@/menu/sysinterface'))
+const PictureController = asyncComponent(() => import('@/menu/picturecontroller'))
+const ModalController = asyncComponent(() => import('@/menu/modalconfig/controller'))
+const StyleCombController = asyncComponent(() => import('@/menu/stylecombcontroller'))
+const StyleCombControlButton = asyncComponent(() => import('@/menu/stylecombcontrolbutton'))
+const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
+
+sessionStorage.setItem('isEditState', 'true')
+sessionStorage.setItem('editMenuType', 'menu') // 缂栬緫鑿滃崟绫诲瀷
+document.body.className = ''
+window.GLOB.UserComponentMap = new Map() // 缂撳瓨鐢ㄦ埛鑷畾涔夌粍浠�
+
+class MenuDesign extends Component {
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    MenuId: '',
+    MenuName: '',
+    MenuNo: '',
+    tableFields: [],
+    delButtons: [],
+    copyButtons: [],
+    thawButtons: [],
+    activeKey: 'basedata',
+    menuloading: false,
+    oriConfig: null,
+    openEdition: '',
+    config: null,
+    popBtn: null,             // 寮圭獥鏍囩椤�
+    visible: false,
+    customComponents: [],
+    settingshow: true,
+    controlshow: true,
+  }
+
+  UNSAFE_componentWillMount() {
+    try {
+      let param = JSON.parse(window.decodeURIComponent(window.atob(this.props.match.params.param)))
+
+      if (param.type === 'app') {
+        sessionStorage.setItem('appId', param.ID || '')
+        sessionStorage.setItem('lang', param.lang || 'zh-CN')
+        sessionStorage.setItem('kei_no', param.kei_no || '')
+        sessionStorage.setItem('link_type', param.link_type || 'true')
+        sessionStorage.setItem('role_type', param.role_type || 'true')
+        sessionStorage.setItem('login_types', param.login_types || 'true')
+      } else if (param.type === 'view') {
+        this.setState({
+          MenuId: param.MenuID
+        })
+      }
+      this.getAppMessage()
+    } catch {
+      notification.warning({
+        top: 92,
+        message: '鑿滃崟淇℃伅瑙f瀽閿欒锛�',
+        duration: 5
+      })
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('delButtons', this.delButtons)
+    MKEmitter.addListener('thawButtons', this.thawButtons)
+    MKEmitter.addListener('copyButtons', this.copyButtons)
+    MKEmitter.addListener('changePopview', this.initPopview)
+    MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
+    MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
+    setTimeout(() => {
+      this.updateCustomComponent()
+      this.getAppPictures()
+    }, 1000)
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('delButtons', this.delButtons)
+    MKEmitter.removeListener('thawButtons', this.thawButtons)
+    MKEmitter.removeListener('copyButtons', this.copyButtons)
+    MKEmitter.removeListener('changePopview', this.initPopview)
+    MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
+    MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
+  }
+
+  getAppMessage = () => {
+    Api.getSystemConfig({
+      func: 's_get_keyids',
+      bid: sessionStorage.getItem('appId')
+    }).then(res => {
+      if (!res) {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+        return
+      }
+
+      let homeId = ''
+      if (this.state.MenuId) {
+        homeId = this.state.MenuId
+      } else {
+        let appViewList = []
+        if (res.data && res.data.length > 0) {
+          appViewList = res.data
+          appViewList.forEach(item => {
+            if (item.keys_type === 'index') {
+              homeId = item.keys_id
+            }
+          })
+        }
+
+        if (!homeId) {
+          homeId = Utils.getuuid()
+
+          let param = {
+            func: 's_kei_link_keyids_addupt',
+            BID: sessionStorage.getItem('appId'),
+            exec_type: 'y',
+            LText: ''
+          }
+
+          appViewList.unshift({
+            appkey: window.GLOB.appkey || '',
+            bid: sessionStorage.getItem('appId') || '',
+            kei_no: sessionStorage.getItem('kei_no') || '',
+            keys_id: homeId,
+            keys_type: 'index',
+            remark: '棣栭〉'
+          })
+
+          param.LText = appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
+          param.LText = param.LText.join(' union all ')
+          param.LText = Utils.formatOptions(param.LText)
+    
+          param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+          param.secretkey = Utils.encrypt('', param.timestamp)
+
+          Api.getSystemConfig(param).then(result => {
+            if (!result.status) {
+              notification.warning({
+                top: 92,
+                message: result.message,
+                duration: 5
+              })
+            }
+          })
+        }
+
+        sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
+      }
+      this.props.history.replace('/pcdesign/' + window.btoa(window.encodeURIComponent(JSON.stringify({MenuID: homeId, type: 'view'}))))
+      
+      this.setState({MenuId: homeId}, () => {
+        this.getMenuParam()
+      })
+    })
+  }
+
+  getAppPictures = () => {
+    Api.getSystemConfig({
+      func: 's_url_db_adduptdel',
+      PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
+      PageSize: 0,   // 0 浠h〃鍏ㄩ儴
+      typecharone: 'image',
+      type: 'search'
+    }).then(res => {
+      if (res.status) {
+        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
+      }
+
+      Api.getSystemConfig({
+        func: 's_url_db_adduptdel',
+        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
+        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
+        typecharone: 'video',
+        type: 'search'
+      }).then(res => {
+        if (res.status) {
+          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
+        }
+      })
+    })
+  }
+
+  updateCustomComponent = () => {
+    Api.getSystemConfig({
+      func: 's_get_custom_components',
+      typecharone: ''
+    }).then(res => {
+      let coms = []
+      if (res.cus_list && res.cus_list.length > 0) {
+        res.cus_list.forEach(item => {
+          let config = ''
+
+          try {
+            config = JSON.parse(window.decodeURIComponent(window.atob(item.long_param)))
+          } catch (e) {
+            console.warn('Parse Failure')
+            config = ''
+          }
+
+          if (!config || !item.c_name) return
+
+          window.GLOB.UserComponentMap.set(item.c_id, item.c_name)
+          coms.push({
+            uuid: item.c_id,
+            type: 'menu',
+            title: item.c_name,
+            url: item.images,
+            component: config.type,
+            subtype: config.subtype,
+            width: config.width || 24,
+            config
+          })
+        })
+      }
+      this.setState({customComponents: coms})
+      this.getRoleFields()
+    })
+  }
+
+  updateComponentStyle = (parentId, keys, style) => {
+    const { config } = this.state
+
+    if (config.uuid !== parentId) return
+
+    let components = config.components.map(item => {
+      if (keys.includes(item.uuid)) {
+        item.style = {...item.style, ...style}
+      }
+      return item
+    })
+
+    this.setState({
+      config: {...config, components: []}
+    }, () => {
+      this.setState({
+        config: {...config, components: components}
+      })
+    })
+  }
+
+  delButtons = (items) => {
+    const { copyButtons } = this.state
+
+    this.setState({
+      delButtons: [...this.state.delButtons, ...items],
+      copyButtons: copyButtons.filter(item => !items.includes(item.uuid))
+    })
+  }
+
+  copyButtons = (items) => {
+    this.setState({copyButtons: [...this.state.copyButtons, ...items]})
+  }
+  
+  thawButtons = (item) => {
+    this.setState({thawButtons: [...this.state.thawButtons, item]})
+  }
+
+  initPopview = (card, btn) => {
+    // const { oriConfig, config } = this.state
+
+    // let _config = fromJS(config).toJS()
+    // delete _config.tableFields
+
+    // if (!is(fromJS(oriConfig), fromJS(_config))) {
+    //   notification.warning({
+    //     top: 92,
+    //     message: '閰嶇疆宸蹭慨鏀癸紝璇蜂繚瀛橈紒',
+    //     duration: 5
+    //   })
+    //   return
+    // }
+
+    // btn.config = _config
+    // btn.component = card
+
+    // sessionStorage.setItem('editMenuType', 'popview') // 缂栬緫寮圭獥鏍囩
+
+    // this.setState({popBtn: btn, visible: true})
+  }
+
+  closeView = () => {
+    const { oriConfig, config } = this.state
+
+    if (!config) {
+      window.close()
+      return
+    }
+
+    let _config = fromJS(config).toJS()
+    delete _config.tableFields
+
+    if (!is(fromJS(oriConfig), fromJS(_config))) {
+      confirm({
+        title: '閰嶇疆宸蹭慨鏀癸紝鏀惧純淇濆瓨鍚楋紵',
+        content: '',
+        onOk() {
+          window.close()
+        },
+        onCancel() {}
+      })
+    } else {
+      window.close()
+    }
+  }
+
+  getMenuParam = () => {
+    const { MenuId } = this.state
+
+    let param = {
+      func: 'sPC_Get_LongParam',
+      MenuID: MenuId
+    }
+
+    Api.getSystemConfig(param).then(result => {
+      if (result.status) {
+        let config = null
+
+        try {
+          config = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : null
+        } catch (e) {
+          console.warn('Parse Failure')
+          config = null
+        }
+
+        if (!config) {
+          config = {
+            version: 1.0,
+            uuid: MenuId,
+            MenuID: MenuId,
+            Template: 'webPage',
+            easyCode: '',
+            enabled: false,
+            MenuName: '',
+            MenuNo: '',
+            tables: [],
+            components: [],
+            viewType: 'menu',
+            style: {
+              backgroundColor: '#ffffff', backgroundImage: '', paddingLeft: '20px', paddingRight: '20px'
+            }
+          }
+        }
+        
+        config.uuid = MenuId
+        config.MenuID = MenuId
+
+        this.setState({
+          oriConfig: config,
+          config: fromJS(config).toJS(),
+          openEdition: result.open_edition || '',
+        })
+
+        this.props.modifyCustomMenu(config)
+      } else {
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  getMenuMessage = () => {
+    const { config } = this.state
+    let buttons = []
+    let _sort = 1
+
+    let traversal = (components) => {
+      components.forEach(item => {
+        if (item.type === 'tabs') {
+          item.subtabs.forEach(tab => {
+            traversal(tab.components)
+          })
+        } else if (item.type === 'group') {
+          traversal(item.components)
+        } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
+          item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
+            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+            _sort++
+          })
+          item.subcards.forEach(card => {
+            card.elements && card.elements.forEach(cell => {
+              if (cell.eleType !== 'button') return
+              this.checkBtn(cell)
+              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
+              _sort++
+            })
+            card.backElements && card.backElements.forEach(cell => {
+              if (cell.eleType !== 'button') return
+              this.checkBtn(cell)
+              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
+              _sort++
+            })
+          })
+        } else if (item.type === 'line' || item.type === 'bar') {
+          item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
+            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+            _sort++
+          })
+        } else if (item.type === 'table' && item.subtype === 'normaltable') {
+          item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
+            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+            _sort++
+          })
+          item.cols && item.cols.forEach(col => {
+            if (col.type !== 'action') return
+            col.elements.forEach(btn => {
+              this.checkBtn(btn)
+              buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+              _sort++
+            })
+          })
+        }
+      })
+    }
+
+    traversal(config.components)
+
+    return buttons
+  }
+
+  checkBtn = (btn) => {
+    if (['prompt', 'exec', 'pop'].includes(btn.OpenType) && btn.Ot === 'required' && btn.verify && btn.verify.scripts && btn.verify.scripts.length > 0) {
+      let hascheck = false
+      btn.verify.scripts.forEach(item => {
+        if (item.status === 'false') return
+  
+        if (/\$check@|@check\$/ig.test(item.sql)) {
+          hascheck = true
+        }
+      })
+      if (hascheck) {
+        notification.warning({
+          top: 92,
+          message: `鍙�夋嫨澶氳鐨勬寜閽��${btn.label}銆嬩腑 $check@ 鎴� @check$ 灏嗕笉浼氱敓鏁堬紒`,
+          duration: 5
+        })
+      }
+    }
+  }
+
+  filterConfig = (components) => {
+    return components.map(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          tab.components = this.filterConfig(tab.components)
+        })
+      } else if (item.type === 'group') {
+        item.components = this.filterConfig(item.components)
+      } else if (item.type === 'table' && item.subtype === 'normaltable') {
+        item.search = item.search.filter(a => !a.origin)
+        item.action = item.action.filter(a => !a.origin)
+        item.cols = item.cols.filter(a => !a.origin)
+      }
+      return item
+    })
+  }
+
+  submitConfig = () => {
+    const { openEdition, delButtons, copyButtons, thawButtons } = this.state
+    let config = fromJS(this.state.config).toJS()
+
+    if (!config.MenuName || !config.MenuNo || (config.cacheUseful === 'true' && !config.cacheTime)) {
+      notification.warning({
+        top: 92,
+        message: '璇峰畬鍠勮彍鍗曞熀鏈俊鎭紒',
+        duration: 5
+      })
+      return
+    }
+
+    this.setState({
+      menuloading: true
+    })
+
+    setTimeout(() => {
+      config.components = this.filterConfig(config.components)
+
+      if (config.enabled && this.verifyConfig()) {
+        config.enabled = false
+      }
+
+      let _config = fromJS(config).toJS()
+      delete _config.tableFields
+
+      let parMenuId = 'pc' + sessionStorage.getItem('kei_no') + sessionStorage.getItem('lang')
+      let param = {
+        func: 'sPC_TrdMenu_AddUpt',
+        FstID: parMenuId,
+        SndID: parMenuId,
+        ParentID: parMenuId,
+        MenuID: _config.uuid,
+        MenuNo: _config.MenuNo || '',
+        EasyCode: _config.easyCode || '',
+        Template: 'webPage',
+        MenuName: _config.MenuName || '',
+        PageParam: JSON.stringify({Template: 'webPage'}),
+        LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_config))),
+        LText: '',
+        LTexttb: ''
+      }
+
+      param.LText = Utils.formatOptions(param.LText)
+      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+
+      if (openEdition) { // 鐗堟湰绠$悊
+        param.open_edition = openEdition
+      }
+
+      let btnParam = {             // 娣诲姞鑿滃崟鎸夐挳
+        func: 'sPC_Button_AddUpt',
+        Type: 40,                  // 娣诲姞鑿滃崟涓嬬殑鎸夐挳type涓�40锛屾寜閽笅鐨勬寜閽畉ype涓�60
+        ParentID: _config.uuid,
+        MenuNo: _config.MenuNo,
+        Template: 'webPage',
+        PageParam: '',
+        LongParam: '',
+        LText: []
+      }
+
+      btnParam.LText = this.getMenuMessage()
+      btnParam.LText = btnParam.LText.join(' union all ')
+
+      let btnIds = btnParam.LText // 鐢ㄤ簬澶嶅埗鎸夐挳鐨勮繃婊�
+
+      btnParam.LText = Utils.formatOptions(btnParam.LText)
+      btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
+
+      new Promise(resolve => {
+        // html2canvas(document.getElementById('menu-shell-inner')).then(canvas => {
+        //   let _param = {
+        //     Base64Img: canvas.toDataURL('image/png') // 鑾峰彇鐢熸垚鐨勫浘鐗�
+        //   }
+
+        //   _param.rduri = options.cloudServiceApi
+        //   _param.userid = sessionStorage.getItem('CloudUserID') || ''
+        //   _param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
+
+        //   Api.fileuploadbase64(_param).then(result => {
+        //     if (result.status) {
+        //       let Images = Utils.getcloudurl(result.Images)
+        //       param.PageParam = JSON.stringify({Template: 'webPage', Images})
+        //       resolve(true)
+        //     } else {
+        //       notification.warning({
+        //         top: 92,
+        //         message: result.ErrMesg,
+        //         duration: 5
+        //       })
+        //       resolve(false)
+        //     }
+        //   })
+        // })
+        resolve(true)
+      }).then(res => { // 鎸夐挳鍒犻櫎
+        if (!res) return
+
+        if (delButtons.length === 0) {
+          return {
+            status: true
+          }
+        } else {
+          let _param = {
+            func: 'sPC_MainMenu_Del',
+            MenuID: delButtons.join(',')
+          }
+          return Api.getSystemConfig(_param)
+        }
+      }).then(res => { // 鎸夐挳瑙i櫎鍐荤粨
+        if (!res) return
+        if (!res.status) {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        }
+
+        let ids = thawButtons.filter(item => btnIds.indexOf(item) !== -1)
+        if (ids.length === 0) {
+          return {
+            status: true
+          }
+        } else {
+          return Api.getSystemConfig({
+            func: 'sPC_MainMenu_ReDel',
+            MenuID: ids.join(',')
+          })
+        }
+      }).then(res => { // 椤甸潰淇濆瓨
+        if (!res) return
+
+        if (res.status) {
+          return Api.getSystemConfig(param)
+        } else {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        }
+      }).then(res => { // 椤甸潰鎸夐挳鍏崇郴淇濆瓨
+        if (!res) return
+
+        if (res.status) {
+          this.setState({
+            oriConfig: fromJS(_config).toJS(),
+            openEdition: res.open_edition || ''
+          })
+
+          if (btnParam.LText) {
+            return Api.getSystemConfig(btnParam)
+          } else {
+            return {
+              status: true
+            }
+          }
+        } else {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        }
+      }).then(res => { // 鎸夐挳澶嶅埗
+        if (!res) return
+        if (!res.status) {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        }
+
+        if (copyButtons.length === 0) {
+          return {
+            status: true
+          }
+        } else {
+          return new Promise(resolve => {
+            let deffers = copyButtons.map(item => {
+              return new Promise(resolve => {
+                if (btnIds.indexOf(item.uuid) === -1) { // 澶嶅埗鐨勬寜閽凡鍒犻櫎
+                  resolve({
+                    status: true
+                  })
+                  return
+                }
+
+                Api.getSystemConfig({
+                  func: 'sPC_Get_LongParam',
+                  MenuID: item.$originUuid
+                }).then(result => {
+                  if (result.status) {
+                    let _conf = ''
+              
+                    try {
+                      _conf = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : ''
+                    } catch (e) {
+                      console.warn('Parse Failure')
+                      _conf = ''
+                    }
+                    
+                    if (_conf) {
+                      _conf.components = MenuUtils.resetConfig(_conf.components)
+                      _conf.uuid = item.uuid
+                      _conf.MenuID = item.uuid
+                      _conf.Template = 'webPage'
+                    } else {
+                      resolve({
+                        status: true
+                      })
+                      return
+                    }
+
+                    let _param = {
+                      func: 'sPC_ButtonParam_AddUpt',
+                      ParentID: _config.uuid,
+                      MenuID: item.uuid,
+                      MenuNo: '',
+                      Template: 'webPage',
+                      MenuName: item.label,
+                      PageParam: JSON.stringify({Template: 'webPage'}),
+                      LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_conf)))
+                    }
+            
+                    Api.getSystemConfig(_param).then(response => {
+                      resolve(response)
+                    })
+                  }
+                })
+              })
+            })
+            Promise.all(deffers).then(result => {
+              let error = null
+              result.forEach(response => {
+                if (!response.status) {
+                  error = response
+                }
+              })
+    
+              if (error) {
+                notification.warning({
+                  top: 92,
+                  message: error.message,
+                  duration: 5
+                })
+                resolve(false)
+              } else {
+                resolve({
+                  status: true
+                })
+              }
+            })
+          })
+        }
+      }).then(res => {
+        if (res && res.status) {
+          this.setState({
+            delButtons: [],
+            copyButtons: [],
+            thawButtons: [],
+            menuloading: false,
+            config: {...config, components: []}
+          }, () => {
+            this.setState({
+              config: {...this.state.config, components: this.state.oriConfig.components}
+            })
+          })
+          notification.success({
+            top: 92,
+            message: '淇濆瓨鎴愬姛',
+            duration: 2
+          })
+        } else {
+          this.setState({
+            menuloading: false
+          })
+        }
+      })
+    }, 300)
+  }
+
+  getRoleFields = () => {
+    Api.getSystemConfig({func: 'sPC_Get_Roles_sModular'}).then(res => {
+      if (res.status) {
+        let _permFuncField = []
+        let _sysRoles = []
+
+        if (res.Roles && res.Roles.length > 0) {
+          _sysRoles = res.Roles.map(role => {
+            return {
+              uuid: Utils.getuuid(),
+              value: role.RoleID,
+              text: role.RoleName
+            }
+          })
+        }
+
+        if (res.sModular && res.sModular.length > 0) {
+          res.sModular.forEach(field => {
+            if (field.ModularNo) {
+              _permFuncField.push(field.ModularNo)
+            }
+          })
+          _permFuncField = _permFuncField.sort()
+        }
+
+        sessionStorage.setItem('sysRoles', JSON.stringify(_sysRoles))
+        sessionStorage.setItem('permFuncField', JSON.stringify(_permFuncField))
+      }
+    })
+  }
+
+  onEnabledChange = () => {
+    const { config } = this.state
+
+    if (!config || (!config.enabled && this.verifyConfig(true))) {
+      return
+    }
+
+    this.setState({
+      config: {...config, enabled: !config.enabled}
+    })
+  }
+
+  verifyConfig = (show) => {
+    const { config } = this.state
+    let error = ''
+
+    config.components.forEach(item => {
+      if (error) return
+      if (['propcard', 'brafteditor', 'sandbox'].includes(item.subtype) && item.wrap.datatype === 'static') return
+
+      if (item.setting) {
+        if (item.setting.interType === 'system' && item.setting.execute !== 'false' && !item.setting.dataresource) {
+          error = `缁勪欢銆�${item.name}銆嬫湭璁剧疆鏁版嵁婧愶紒`
+        } else if (item.setting.interType === 'system' && item.setting.execute === 'false' && item.scripts.length === 0) {
+          error = `缁勪欢銆�${item.name}銆嬫湭璁剧疆鏁版嵁婧愶紒`
+        } else if (item.setting.interType && !item.setting.primaryKey) {
+          error = `缁勪欢銆�${item.name}銆嬫湭璁剧疆涓婚敭锛乣
+        }
+      }
+      if (item.type === 'bar' || item.type === 'line' || item.type === 'pie') {
+        if (!item.plot.Xaxis) {
+          error = `缁勪欢銆�${item.name}銆嬪浘琛ㄥ瓧娈靛皻鏈缃紒`
+        }
+      }
+    })
+
+    if (show && error) {
+      notification.warning({
+        top: 92,
+        message: error,
+        duration: 5
+      })
+    }
+
+    return error
+  }
+
+  // 鏇存柊閰嶇疆淇℃伅
+  updateConfig = (config) => {
+    this.setState({
+      config: config
+    })
+
+    this.props.modifyCustomMenu(config)
+  }
+
+  insert = (item) => {
+    let config = fromJS(this.state.config).toJS()
+
+    config.components.push(item)
+
+    this.setState({config})
+    this.props.modifyCustomMenu(config)
+  }
+
+  /**
+   * @description 鏇存柊甯哥敤琛ㄤ俊鎭紝蹇嵎娣诲姞鍚庢洿鏂伴厤缃俊鎭�
+   */
+  updatetable = (config, fields) => {
+    const { tableFields } = this.state
+
+    config.tableFields = fields ? fields : tableFields
+
+    this.setState({
+      tableFields: fields ? fields : tableFields,
+      config
+    })
+
+    this.props.modifyCustomMenu(config)
+  }
+
+  render () {
+    const { activeKey, settingshow, controlshow, dict, MenuId, config, menuloading, customComponents } = this.state
+
+    return (
+      <ConfigProvider locale={_locale}>
+        <div className={'mk-pc-view '} id="mk-menu-design-view">
+          <DndProvider backend={HTML5Backend}>
+            <div className={'menu-setting ' + (!settingshow ? 'hidden' : '')}>
+              <div className="draw">
+                {settingshow ? <Icon onClick={() => this.setState({settingshow: false})} type="double-left" /> : null}
+                {!settingshow ? <Icon onClick={() => this.setState({settingshow: true})} type="double-right" /> : null}
+              </div>
+              <div className="pc-setting-tools">
+                <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
+                  {/* 鍩烘湰淇℃伅 */}
+                  <Panel header={dict['mob.basemsg']} key="basedata">
+                    {/* 鑿滃崟淇℃伅 */}
+                    {config ? <MenuForm
+                      dict={dict}
+                      config={config}
+                      MenuId={MenuId}
+                      updateConfig={this.updateConfig}
+                    /> : null}
+                    {/* 琛ㄥ悕娣诲姞 */}
+                    {config ? <TableComponent config={config} updatetable={this.updatetable}/> : null}
+                  </Panel>
+                  {/* 缁勪欢娣诲姞 */}
+                  <Panel header={dict['mob.component']} key="component">
+                    <SourceWrap MenuType={'pc'} />
+                  </Panel>
+                  {customComponents && customComponents.length ? <Panel header="鑷畾涔夌粍浠�" key="cuscomponent">
+                    <SourceWrap components={customComponents} MenuType={'pc'} />
+                  </Panel> : null}
+                  <Panel header={'椤甸潰鑳屾櫙'} key="background">
+                    {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
+                  </Panel>
+                  <Panel header={'椤甸潰鍐呰竟璺�'} key="padding">
+                    {config ? <PaddingController config={config} updateConfig={this.updateConfig} /> : null}
+                  </Panel>
+                </Collapse>
+              </div>
+            </div>
+            <div className={'menu-control ' + (!controlshow ? 'hidden' : '')}>
+              <div className="draw">
+                {controlshow ? <Icon onClick={() => this.setState({controlshow: false})} type="double-right" /> : null}
+                {!controlshow ? <Icon onClick={() => this.setState({controlshow: true})} type="double-left" /> : null}
+              </div>
+              <Button type="primary" onClick={this.submitConfig} loading={menuloading}>{dict['mob.save']}</Button>
+              <Switch className="big" checkedChildren={dict['mob.enable']} unCheckedChildren={dict['mob.disable']} checked={config && config.enabled} onChange={this.onEnabledChange} />
+              <PasteController type="menu" Tab={null} insert={this.insert} />
+              <StyleCombControlButton menu={config} />
+              <SysInterface config={config} updateConfig={this.updateConfig}/>
+              <PictureController/>
+              <Button type="default" onClick={this.closeView}>{dict['mob.return']}</Button>
+            </div>
+            <div className={'menu-body ' + (menuloading ? 'saving' : '')}>
+              {config && config.components ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
+            </div>
+          </DndProvider>
+          <StyleController />
+          <StyleCombController />
+          <ModalController />
+        </div>
+      </ConfigProvider>
+    )
+  }
+}
+
+const mapStateToProps = () => {
+  return {}
+}
+
+const mapDispatchToProps = (dispatch) => {
+  return {
+    modifyCustomMenu: (customMenu) => dispatch(modifyCustomMenu(customMenu))
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(MenuDesign)
\ No newline at end of file
diff --git a/src/views/pcdesign/index.scss b/src/views/pcdesign/index.scss
new file mode 100644
index 0000000..590b099
--- /dev/null
+++ b/src/views/pcdesign/index.scss
@@ -0,0 +1,190 @@
+.mk-pc-view {
+  background: #000;
+  min-height: 100vh;
+  .menu-setting {
+    position: fixed;
+    left: 0;
+    top: 0px;
+    z-index: 10;
+    transition: left 0.3s;
+
+    .draw {
+      position: absolute;
+      z-index: 1;
+      background: #ffffff;
+      right: -20px;
+      top: 0px;
+      box-shadow: 0 0 1px #959595;
+      border-radius: 0 2px 2px 0px;
+
+      i {
+        padding: 12px 3px;
+      }
+    }
+
+    .pc-setting-tools {
+      height: 100vh;
+      width: 300px;
+      background: #ffffff;
+      box-shadow: 0px 2px 5px #bcbcbc;
+      overflow-y: auto;
+      overflow-x: hidden;
+
+      > .ant-collapse {
+        background-color: #ffffff;
+        .ant-collapse-item.ant-collapse-item-active {
+          border-bottom: 1px solid #d9d9d9;
+        }
+        .ant-collapse-header {
+          padding: 11px 16px 10px 40px;
+          border-bottom: 1px solid #d9d9d9;
+          background: #1890ff;
+          color: #ffffff;
+        }
+        .ant-collapse-content-box {
+          .ant-form-item {
+            margin-bottom: 10px;
+          }
+          .model-table-tablemanage-view {
+            >.ant-list {
+              margin-top: 20px;
+              .ant-list-item {
+                display: -webkit-box;
+                padding-right: 20px;
+                position: relative;
+                padding-left: 5px;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                -webkit-line-clamp: 2;
+                -webkit-box-orient: vertical;
+                min-height: 55px;
+                width: 100%;
+                .anticon {
+                  position: absolute;
+                  top: 0px;
+                  right: 0px;
+                  padding: 3px 3px 10px 10px;
+                  cursor: pointer;
+                }
+              }
+            }
+            >.tables {
+              width: 66.66666667%!important;
+            }
+            >.ant-form-item-label {
+              width: 33.33333333%;
+            }
+          }
+        }
+      }
+  
+      >.ant-tabs {
+        >.ant-tabs-bar {
+          border-bottom: 1px solid #181F29;
+          margin-bottom: 0px;
+          min-height: 48px;
+          .ant-tabs-tab {
+            padding: 14px 16px;
+            color: rgba(255, 255, 255, 0.85);
+          }
+          .ant-tabs-tab-active.ant-tabs-tab {
+            color: #1890ff;
+          }
+        }
+      }
+    }
+    .pc-setting-tools::-webkit-scrollbar {
+      width: 4px;
+    }
+    .pc-setting-tools::-webkit-scrollbar-thumb {
+      border-radius: 5px;
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.08);
+      background: rgba(0, 0, 0, 0.08);
+    }
+    .pc-setting-tools::-webkit-scrollbar-track {
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      border: 1px solid rgba(0, 0, 0, 0.07);
+      background: rgba(0, 0, 0, 0);
+    }
+  }
+  .menu-setting.hidden {
+    left: -300px;
+  }
+
+  .menu-control {
+    position: fixed;
+    right: 0;
+    top: 0px;
+    height: 100vh;
+    padding: 20px 10px;
+    background: #ffffff;
+    z-index: 10;
+    transition: right 0.3s;
+    box-shadow: 0px 0px 5px #bcbcbc;
+
+    .draw {
+      position: absolute;
+      z-index: 1;
+      background: #ffffff;
+      left: -21px;
+      top: 0px;
+      box-shadow: 0 0 1px #959595;
+      border-radius: 0 2px 2px 0px;
+
+      i {
+        padding: 12px 3px;
+      }
+    }
+    div:not(.draw), button:not(.ant-switch) {
+      display: block!important;
+      margin-bottom: 15px;
+      width: 100%;
+    }
+    .ant-switch.big {
+      min-width: 60px;
+      height: 24px;
+      line-height: 24px;
+      margin-bottom: 15px;
+      .ant-switch-inner {
+        font-size: 14px;
+      }
+    }
+    .ant-switch.big:after {
+      width: 22px;
+      height: 22px;
+    }
+  }
+  .menu-control.hidden {
+    right: -130px;
+  }
+
+  .menu-body {
+    width: 100vw;
+    height: 100vh;
+    overflow-x: hidden;
+    position: relative;
+    background: #ffffff;
+    padding: 0px;
+    overflow-y: auto;
+  }
+  .menu-body.saving {
+    .anticon-tool {
+      display: none;
+    }
+  }
+  .menu-body::-webkit-scrollbar {
+    width: 7px;
+  }
+  .menu-body::-webkit-scrollbar-thumb {
+    border-radius: 5px;
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.08);
+    background: rgba(0, 0, 0, 0.08);
+  }
+  .menu-body::-webkit-scrollbar-track {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+    border: 1px solid rgba(0, 0, 0, 0.07);
+    background: rgba(0, 0, 0, 0);
+  }
+}
\ No newline at end of file
diff --git a/src/views/pcdesign/menuform/index.jsx b/src/views/pcdesign/menuform/index.jsx
new file mode 100644
index 0000000..13163ce
--- /dev/null
+++ b/src/views/pcdesign/menuform/index.jsx
@@ -0,0 +1,161 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Radio, Icon, Tooltip, InputNumber } from 'antd'
+
+import './index.scss'
+
+class CustomMenuForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object, // 瀛楀吀椤�
+    config: PropTypes.object,
+    MenuId: PropTypes.string,
+    updateConfig: PropTypes.func
+  }
+
+  state = {}
+
+  UNSAFE_componentWillMount () {
+
+  }
+
+  UNSAFE_componentWillReceiveProps(nextProps) {
+    const { config } = this.props
+    if (!config && nextProps.config) {
+      this.props.form.setFieldsValue({easyCode: nextProps.config.easyCode})
+    }
+  }
+
+  // 涓�浜岀骇鑿滃崟鍒囨崲
+  selectChange = (key, value) => {
+    const { config } = this.props
+
+    if (key === 'cacheUseful') {
+      this.props.updateConfig({...config, cacheUseful: value})
+    } else if (key === 'timeUnit') {
+      this.props.updateConfig({...config, timeUnit: value})
+    }
+  }
+
+  // 鑿滃崟鍚嶇О
+  changeName = (e) => {
+    this.props.updateConfig({...this.props.config, MenuName: e.target.value})
+  }
+
+  // 鑿滃崟鍙傛暟
+  changeNo = (e) => {
+    this.props.updateConfig({...this.props.config, MenuNo: e.target.value})
+  }
+
+  // 鍔╄鐮�
+  changeEasyCode = (e) => {
+    this.props.updateConfig({...this.props.config, easyCode: e.target.value})
+  }
+
+  changeCacheDay = (val) => {
+    if (typeof(val) !== 'number') {
+      val = ''
+    }
+    this.props.updateConfig({...this.props.config, cacheTime: val})
+  }
+
+  render() {
+    const { dict, config } = this.props
+    const { getFieldDecorator } = this.props.form
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout} className="custom-menu-form">
+        <Row>
+          <Col span={24}>
+            <Form.Item label={dict['mob.menu'] + dict['mob.name']}>
+              {getFieldDecorator('MenuName', {
+                initialValue: config.MenuName,
+                rules: [
+                  {
+                    required: true,
+                    message: dict['mob.required.input'] + dict['mob.menu'] + dict['mob.name'] + '!'
+                  }
+                ]
+              })(<Input placeholder="" autoComplete="off" onChange={this.changeName}/>)}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label={dict['mob.menu'] + dict['mob.param']}>
+              {getFieldDecorator('MenuNo', {
+                initialValue: config.MenuNo,
+                rules: [
+                  {
+                    required: true,
+                    message: dict['mob.required.input'] + dict['mob.menu'] + dict['mob.param'] + '!'
+                  }
+                ]
+              })(<Input placeholder="" autoComplete="off" onChange={this.changeNo}/>)}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="瀵逛簬涓嶇粡甯告�у彉鍔ㄧ殑淇℃伅锛岀紦瀛樻暟鎹湁鍔╀簬鎻愰珮鏌ヨ鏁堢巼銆�">
+                <Icon type="question-circle" />
+                缂撳瓨鏁版嵁
+              </Tooltip>
+            }>
+              {getFieldDecorator('cacheUseful', {
+                initialValue: config.cacheUseful || 'false'
+              })(
+                <Radio.Group onChange={(e) => {this.selectChange('cacheUseful', e.target.value)}}>
+                  <Radio value="true">浣跨敤</Radio>
+                  <Radio value="false">涓嶄娇鐢�</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          {config.cacheUseful === 'true' ? <Col span={24}>
+            <Form.Item label="鍗曚綅">
+              {getFieldDecorator('timeUnit', {
+                initialValue: config.timeUnit || 'day'
+              })(
+                <Radio.Group onChange={(e) => {this.selectChange('timeUnit', e.target.value)}}>
+                  <Radio value="day">澶�</Radio>
+                  <Radio value="hour">灏忔椂</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col> : null}
+          {config.cacheUseful === 'true' ? <Col span={24}>
+            <Form.Item label="鏃堕暱">
+              {getFieldDecorator('cacheTime', {
+                initialValue: config.cacheTime,
+                rules: [
+                  {
+                    required: true,
+                    message: dict['mob.required.input'] + '鏃堕暱!'
+                  }
+                ]
+              })(
+                <InputNumber min={1} max={config.timeUnit !== 'hour' ? 7 : 23} precision={0} onChange={this.changeCacheDay}/>
+              )}
+            </Form.Item>
+          </Col> : null}
+          <Col span={24}>
+            <Form.Item label={dict['mob.menu.easycode']}>
+              {getFieldDecorator('easyCode', {
+                initialValue: config.easyCode
+              })(<Input placeholder="" autoComplete="off" onChange={this.changeEasyCode}/>)}
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(CustomMenuForm)
\ No newline at end of file
diff --git a/src/views/pcdesign/menuform/index.scss b/src/views/pcdesign/menuform/index.scss
new file mode 100644
index 0000000..71a1a33
--- /dev/null
+++ b/src/views/pcdesign/menuform/index.scss
@@ -0,0 +1,10 @@
+.custom-menu-form {
+  .anticon-question-circle {
+    color: #c49f47;
+    position: relative;
+    left: -3px;
+  }
+  .ant-input-number {
+    width: 100%;
+  }
+}
\ No newline at end of file

--
Gitblit v1.8.0