king
2022-01-20 a9a5211f48607fa7ac5bae91233f3a5e6df43587
2022-01-20
16个文件已修改
6个文件已添加
1561 ■■■■■ 已修改文件
public/options.json 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/back.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/index.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/pastecontroller/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/replaceField/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/sysinterface/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modulesource/dragsource/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/bgcontroller/index.jsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/index.jsx 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/submutilform/index.jsx 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/imdesign/index.jsx 597 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/imdesign/index.scss 341 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/imdesign/menuform/index.jsx 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/imdesign/menuform/index.scss 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/imdesign/options.jsx 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/index.jsx 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/index.scss 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.jsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/rolemanage/index.jsx 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/options.json
@@ -1,17 +1,17 @@
{
  "appId": "202108312122504607B107A83F55B40C98CCF",
  "appkey": "20210831212235413F287EC3BF489424496C8",
  "appId": "201912040924165801464FF1788654BC5AC73",
  "appkey": "20191106103859640976D6E924E464D029CF0",
  "mainSystemApi": "http://sso.mk9h.cn/cloud/webapi/dostars",
  "systemType": "",
  "externalDatabase": "false",
  "lineColor": "",
  "filter": "false",
  "defaultApp": "mkindustry",
  "defaultApp": "mk",
  "defaultLang": "zh-CN",
  "WXAppID": "",
  "debugger": false,
  "licenseKey": "7EFE13KIKKILIJ7C8CFC",
  "probation": "2021-12-31",
  "host": "http://demo.mk9h.cn",
  "service": "erp_new/"
  "licenseKey": "",
  "probation": "",
  "host": "http://qingqiumarket.cn",
  "service": "MKWMS/"
}
src/assets/img/back.jpg
src/index.js
@@ -34,7 +34,7 @@
  })())
}
const render  = Component => {
const render = Component => {
  ReactDOM.render(
    <Provider store={store}>
      <Component/>
src/menu/pastecontroller/index.jsx
@@ -109,7 +109,7 @@
    const { visible } = this.state
    return (
      <div style={{display: 'inline-block'}}>
      <div className="mk-view-paste" style={{display: 'inline-block'}}>
        <Button style={{borderColor: '#40a9ff', color: '#40a9ff'}} onClick={() => {this.setState({visible: true})}}><SnippetsOutlined />粘贴</Button>
        <Modal
          title="粘贴"
src/menu/replaceField/index.jsx
@@ -358,7 +358,7 @@
    const { visible, loadingTable, tables, confirming } = this.state
    return (
      <div style={{display: 'inline-block'}}>
      <div className="mk-replace-field" style={{display: 'inline-block'}}>
        <Button className="mk-border-yellow" icon="swap" loading={loadingTable} onClick={this.trigger}>字段替换</Button>
        <Modal
          title="字段替换"
src/menu/sysinterface/index.jsx
@@ -177,7 +177,7 @@
    const { visible, setvisible, columns, interfaces, card } = this.state
    return (
      <div style={{display: 'inline-block'}}>
      <div className="mk-sys-interface" style={{display: 'inline-block'}}>
        <Button className="mk-border-green" onClick={this.trigger}><ApiOutlined /> 接口管理</Button>
        <Modal
          title="接口管理"
src/mob/modulesource/dragsource/index.jsx
@@ -6,7 +6,7 @@
const MobSourceElement = ({item, triggerDel}) => {
  const [, drag] = useDrag({ item })
  return (
    <div className="menu-source-item">
    <div className={'menu-source-item ' + item.component + '-' + item.subtype}>
      <div className="property"><span>{item.title}</span>{item.config ? <CloseCircleOutlined onClick={() => triggerDel(item)}/> : null}</div>
      <img ref={drag} src={item.url} alt=""/>
    </div>
src/pc/bgcontroller/index.jsx
@@ -23,7 +23,7 @@
    backgroundImage: '',
    backgroundSize: '',
    backgroundRepeat: '',
    backgroundPositon: '',
    backgroundPosition: '',
    opacity: '',
  }
@@ -42,7 +42,7 @@
      backgroundImage: bgImg,
      backgroundSize: config.style.backgroundSize || '100%',
      backgroundRepeat: config.style.backgroundRepeat || 'repeat',
      backgroundPositon: config.style.backgroundPositon || 'center'
      backgroundPosition: config.style.backgroundPosition || 'center'
    })
  }
@@ -114,18 +114,18 @@
  backgroundPositonChange = (val) => {
    this.setState({
      backgroundPositon: val
      backgroundPosition: val
    })
    let config = fromJS(this.props.config).toJS()
    config.style.backgroundPositon = val
    config.style.backgroundPosition = val
    this.props.updateConfig(config)
  }
  render () {
    const { config } = this.props
    const { backgroundColor, backgroundImage, backgroundSize, backgroundRepeat, backgroundPositon } = this.state
    const { backgroundColor, backgroundImage, backgroundSize, backgroundRepeat, backgroundPosition } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -143,6 +143,7 @@
          <Form.Item
            colon={false}
            label="宽度"
            className="normal-view"
          >
            <StyleInput defaultValue={config.style.width || '100%'} options={['px', '%', 'vw']} onChange={(val) => this.changePadding(val, 'width')}/>
          </Form.Item>
@@ -170,58 +171,66 @@
            </Select>
          </Form.Item>
          <Form.Item colon={false} label="位置">
            <Select defaultValue={backgroundPositon} onChange={this.backgroundPositonChange}>
            <Select defaultValue={backgroundPosition} onChange={this.backgroundPositonChange}>
              <Option value="center">center</Option>
              <Option value="top">top</Option>
              <Option value="bottom">bottom</Option>
            </Select>
          </Form.Item>
          <p style={{borderBottom: '1px solid #eaeaea', color: '#40a9ff'}}>内边距</p>
          <p className="normal-view" style={{borderBottom: '1px solid #eaeaea', color: '#40a9ff'}}>内边距</p>
          <Form.Item
            colon={false}
            className="normal-view"
            label={<ArrowUpOutlined title="上边距"/>}
          >
            <StyleInput defaultValue={config.style.paddingTop || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingTop')}/>
          </Form.Item>
          <Form.Item
            colon={false}
            className="normal-view"
            label={<ArrowDownOutlined title="下边距"/>}
          >
            <StyleInput defaultValue={config.style.paddingBottom || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingBottom')}/>
          </Form.Item>
          <Form.Item
            colon={false}
            className="normal-view"
            label={<ArrowLeftOutlined title="左边距"/>}
          >
            <StyleInput defaultValue={config.style.paddingLeft || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingLeft')}/>
          </Form.Item>
          <Form.Item
            colon={false}
            className="normal-view"
            label={<ArrowRightOutlined title="右边距"/>}
          >
            <StyleInput defaultValue={config.style.paddingRight || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingRight')}/>
          </Form.Item>
          <p style={{borderBottom: '1px solid #eaeaea', color: '#40a9ff'}}>外边距</p>
          <p className="normal-view" style={{borderBottom: '1px solid #eaeaea', color: '#40a9ff'}}>外边距</p>
          <Form.Item
            colon={false}
            className="normal-view"
            label={<ArrowUpOutlined title="上边距"/>}
          >
            <StyleInput defaultValue={config.style.marginTop || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'marginTop')}/>
          </Form.Item>
          <Form.Item
            colon={false}
            className="normal-view"
            label={<ArrowDownOutlined title="下边距"/>}
          >
            <StyleInput defaultValue={config.style.marginBottom || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'marginBottom')}/>
          </Form.Item>
          <Form.Item
            colon={false}
            className="normal-view"
            label={<ArrowLeftOutlined title="左边距"/>}
          >
            <StyleInput defaultValue={config.style.marginLeft || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'marginLeft')}/>
          </Form.Item>
          <Form.Item
            colon={false}
            className="normal-view"
            label={<ArrowRightOutlined title="右边距"/>}
          >
            <StyleInput defaultValue={config.style.marginRight || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'marginRight')}/>
src/router/index.js
@@ -16,6 +16,7 @@
const AppCheck = asyncLoadComponent(() => import('@/views/appcheck'))
const PCDesign = asyncLoadComponent(() => import('@/views/pcdesign'))
const MobDesign = asyncLoadComponent(() => import('@/views/mobdesign'))
const ImDesign = asyncLoadComponent(() => import('@/views/imdesign'))
const MenuDesign = asyncLoadComponent(() => import('@/views/menudesign'))
const BillPrint = asyncLoadComponent(() => import('@/views/billprint'))
const PrintT = asyncLoadComponent(() => import('@/views/printTemplate'))
@@ -33,6 +34,7 @@
  {path: '/appcheck', name: 'appcheck', component: AppCheck, auth: true},
  {path: '/pcdesign/:param', name: 'pcdesign', component: PCDesign, auth: true},
  {path: '/mobdesign/:param', name: 'mobdesign', component: MobDesign, auth: true},
  {path: '/imdesign/:param', name: 'imdesign', component: ImDesign, 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},
src/views/appmanage/index.jsx
@@ -467,8 +467,8 @@
              } catch (e) {
                _param = {}
              }
              cell.copyright = _param.copyright || ''
              cell.logo = _param.logo || ''
              // cell.copyright = _param.copyright || ''
              // cell.logo = _param.logo || ''
              cell.apptype = _param.apptype || ''
              cell.delay = _param.delay || 0
              cell.statusBarColor = _param.statusBarColor || 'black'
@@ -477,10 +477,17 @@
              if (cell.adapter && (cell.adapter === 'true' || cell.adapter === 'false')) {
                cell.adapter = ''
              }
              cell.userbind = _param.userbind || ''
              cell.instantMessage = _param.instantMessage || ''
            }
            if (!cell.adapter && (cell.user_binding || cell.apptype)) {
              cell.adapter = 'app,weixin'
            if (cell.user_binding !== 'true') {
              cell.user_binding = 'false'
            }
            if (!cell.adapter && cell.apptype) {
              cell.adapter = 'app'
            }
            return cell
@@ -616,9 +623,16 @@
    let sublist = fromJS(selectApp.sublist).toJS()
    sublist = sublist.filter(item => item.ID !== record.ID)
    sublist = sublist.map(item => {
      if (item.typename !== 'pc') {
        item.userbind = md5(selectApp.kei_no + item.typename + item.lang).replace(/^.{8}/, 'userbind')
        item.instantMessage = md5(selectApp.kei_no + item.typename + item.lang).replace(/^.{14}/, 'instantmessage')
      }
      return item
    })
    // param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','${item.login_types || 'true'}','${item.link_type || 'true'}','${item.role_type || 'true'}','${item.lang || 'zh-CN'}'`)
    param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || ''}','${item.sms_id || ''}','${window.btoa(window.encodeURIComponent(JSON.stringify({copyright: item.copyright || '', logo: item.logo || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', sysBgColor: item.sysBgColor || '#ffffff', adapter: item.adapter || ''})))}'`)
    // 子应用ID、typename、应用ID、CloudUserID、appkey、login_types(是否需要登录,已弃用)、link_type(是否使用短连接,已弃用)、role_type(是否使用角色管理)、lang、css(皮肤)、title(标题)、favicon(图标)、user_binding(用户绑定)、sms_id(短信模板ID)、自定义
    param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || 'false'}','','${window.btoa(window.encodeURIComponent(JSON.stringify({userbind: item.userbind || '', instantMessage: item.instantMessage || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', sysBgColor: item.sysBgColor || '#ffffff', adapter: item.adapter || ''})))}'`)
    param.LText = param.LText.join(' union all ')
    param.LText = Utils.formatOptions(param.LText)
    
@@ -841,8 +855,15 @@
      param.secretkey = Utils.encrypt('', param.timestamp)
      if (visible === 'edit') {
        // param.LText = selectApp.sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','${item.login_types || 'true'}','${item.link_type || 'true'}','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}'`)
        param.LText = selectApp.sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || ''}','${item.sms_id || ''}','${window.btoa(window.encodeURIComponent(JSON.stringify({copyright: item.copyright || '', logo: item.logo || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', sysBgColor: item.sysBgColor || '#ffffff', adapter: item.adapter || ''})))}'`)
        selectApp.sublist = selectApp.sublist.map(item => {
          if (item.typename !== 'pc') {
            item.userbind = md5(selectApp.kei_no + item.typename + item.lang).replace(/^.{8}/, 'userbind')
            item.instantMessage = md5(selectApp.kei_no + item.typename + item.lang).replace(/^.{14}/, 'instantmessage')
          }
          return item
        })
        param.LText = selectApp.sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || 'false'}','','${window.btoa(window.encodeURIComponent(JSON.stringify({userbind: item.userbind || '', instantMessage: item.instantMessage || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', sysBgColor: item.sysBgColor || '#ffffff', adapter: item.adapter || ''})))}'`)
        param.LText = param.LText.join(' union all ')
        param.LText = Utils.formatOptions(param.LText)
      }
@@ -927,8 +948,15 @@
        })
      }
      // param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','${item.login_types || 'true'}','${item.link_type || 'true'}','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}'`)
      param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || ''}','${item.sms_id || ''}','${window.btoa(window.encodeURIComponent(JSON.stringify({copyright: item.copyright || '', logo: item.logo || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', sysBgColor: item.sysBgColor || '#ffffff', adapter: item.adapter || ''})))}'`)
      sublist = sublist.map(item => {
        if (item.typename !== 'pc') {
          item.userbind = md5(selectApp.kei_no + item.typename + item.lang).replace(/^.{8}/, 'userbind')
          item.instantMessage = md5(selectApp.kei_no + item.typename + item.lang).replace(/^.{14}/, 'instantmessage')
        }
        return item
      })
      param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','false','false','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}','${item.user_binding || 'false'}','','${window.btoa(window.encodeURIComponent(JSON.stringify({userbind: item.userbind || '', instantMessage: item.instantMessage || '', apptype: item.apptype || '', delay: item.delay || 0, statusBarColor: item.statusBarColor || 'black', sysBgColor: item.sysBgColor || '#ffffff', adapter: item.adapter || ''})))}'`)
      param.LText = param.LText.join(' union all ')
      param.LText = Utils.formatOptions(param.LText)
@@ -1046,13 +1074,8 @@
                let css = skinStyle[item.css] ? skinStyle[item.css].name : ''
                let color = skinStyle[item.css] ? skinStyle[item.css].color : '#e8e8e8'
                let binding = ''
                if (item.user_binding) {
                  if (item.user_binding.indexOf('uname_pwd') > -1) {
                    binding = '用户名'
                  }
                  if (item.user_binding.indexOf('sms_vcode') > -1) {
                    binding = binding ? binding + ',手机号' : '手机号'
                  }
                if (item.user_binding === 'true') {
                  binding = '需要'
                }
                return (
                  <div className="sub-app" key={index} style={{borderColor: color}}>
@@ -1108,18 +1131,6 @@
                        <div className="app-item">
                          <div className="label">网站头像:</div>
                          <div className="content">{item.favicon ? <img style={{width: '18px', height: '18px'}} src={item.favicon} alt="" /> : '无'}</div>
                        </div>
                      </Col>
                      <Col span={12}>
                        <div className="app-item">
                          <div className="label">版权:</div>
                          <div className="content">{item.copyright || '无'}</div>
                        </div>
                      </Col>
                      <Col span={12}>
                        <div className="app-item">
                          <div className="label">LOGO:</div>
                          <div className="content">{item.logo ? <img style={{height: '18px'}} src={item.logo} alt="" /> : '无'}</div>
                        </div>
                      </Col>
                    </Row>
src/views/appmanage/index.scss
@@ -66,7 +66,7 @@
      .sub-app {
        margin: 10px 10px 25px;
        border: 1px solid #e8e8e8;
        height: 210px;
        height: 185px;
        border-radius: 4px;
        padding-top: 10px;
@@ -95,7 +95,7 @@
        .action {
          text-align: right;
          padding: 5px 20px;
          margin-top: 5px;
          margin-top: 10px;
          border-top: 1px solid #e8e8e8;
          .ant-typography-copy {
src/views/appmanage/submutilform/index.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Select, Radio, Input, Tooltip, notification, InputNumber, Checkbox } from 'antd'
import { Form, Row, Col, Select, Radio, Input, Tooltip, InputNumber, Checkbox } from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
import asyncComponent from '@/utils/asyncComponent'
@@ -16,26 +16,19 @@
    inputSubmit: PropTypes.func  // input回车提交
  }
  state = {msgs: [], typename: 'mob', user_binding: [], adapters: []}
  state = {typename: 'mob', adapters: []}
  UNSAFE_componentWillMount() {
    const { card } = this.props
    let msgs = sessionStorage.getItem('msgTemplate')
    let user_binding = []
    let adapters = []
    let typename = 'mob'
    if (card) {
      typename = card.typename || 'mob'
      user_binding = card.user_binding ? card.user_binding.split(',') : []
      adapters = card.adapter ? card.adapter.split(',') : []
      if (!adapters.includes('weixin') && !adapters.includes('wxmini')) {
        user_binding = []
      }
    }
    this.setState({msgs: JSON.parse(msgs), typename, user_binding, adapters})
    this.setState({typename, adapters})
  }
  /**
@@ -45,31 +38,31 @@
    return new Promise(resolve => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          values.user_binding = values.user_binding ? values.user_binding.join(',') : ''
          // values.user_binding = values.user_binding ? values.user_binding.join(',') : ''
          values.adapter = values.adapter ? values.adapter.join(',') : ''
          if (values.user_binding.indexOf('sms_vcode') > -1 && !values.sms_id) {
            notification.warning({
              top: 92,
              message: '手机号绑定时,需要短信模板!',
              duration: 5
            })
            return
          }
          // if (values.user_binding.indexOf('sms_vcode') > -1 && !values.sms_id) {
          //   notification.warning({
          //     top: 92,
          //     message: '手机号绑定时,需要短信模板!',
          //     duration: 5
          //   })
          //   return
          // }
          resolve(values)
        }
      })
    })
  }
  onChange = (vals) => {
    this.setState({user_binding: vals})
  }
  // onChange = (vals) => {
  //   this.setState({user_binding: vals})
  // }
  onAdapterChange = (vals) => {
    this.setState({adapters: vals})
    if (!vals.includes('weixin') && !vals.includes('wxmini')) {
      this.setState({user_binding: []})
    }
    // if (!vals.includes('weixin') && !vals.includes('wxmini')) {
    //   this.setState({user_binding: []})
    // }
  }
  /**
@@ -83,7 +76,7 @@
  render() {
    const { card, type } = this.props
    const { getFieldDecorator } = this.props.form
    const { msgs, typename, user_binding, adapters } = this.state
    const { typename, adapters } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -175,16 +168,20 @@
          {typename !== 'pc' && (adapters.includes('weixin') || adapters.includes('wxmini')) ? <Col span={12}>
            <Form.Item label="用户绑定">
              {getFieldDecorator('user_binding', {
                initialValue: user_binding
                initialValue: card ? card.user_binding : 'false'
              })(
                <Checkbox.Group onChange={this.onChange}>
                  <Checkbox value="uname_pwd">账号</Checkbox>
                  <Checkbox value="sms_vcode">手机短信</Checkbox>
                </Checkbox.Group>
                <Radio.Group>
                  <Radio value="true">需要</Radio>
                  <Radio value="false">不需要</Radio>
                </Radio.Group>
                // <Checkbox.Group onChange={this.onChange}>
                //   <Checkbox value="uname_pwd">账号</Checkbox>
                //   <Checkbox value="sms_vcode">手机短信</Checkbox>
                // </Checkbox.Group>
              )}
            </Form.Item>
          </Col> : null}
          {typename !== 'pc' && user_binding.includes('sms_vcode') ? <Col span={12}>
          {/* {typename !== 'pc' && user_binding.includes('sms_vcode') ? <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="短信模板可在 云系统->应用服务->开发者中心->短信模板 处添加。">
                <QuestionCircleOutlined className="mk-form-tip" />
@@ -205,7 +202,7 @@
                </Select>
              )}
            </Form.Item>
          </Col> : null}
          </Col> : null} */}
          <Col span={12}>
            <Form.Item label="标题">
              {getFieldDecorator('title', {
@@ -222,7 +219,7 @@
              )}
            </Form.Item>
          </Col>
          {typename !== 'pc' && user_binding.length > 0 ? <Col span={12}>
          {/* {typename !== 'pc' && user_binding.length > 0 ? <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="使用微信授权登录时,绑定用户页面的版权声明,注:可添加html标签。">
                <QuestionCircleOutlined className="mk-form-tip" />
@@ -247,7 +244,7 @@
                <SourceComponent type="picture" placement="right"/>
              )}
            </Form.Item>
          </Col> : null}
          </Col> : null} */}
          {typename !== 'pc' && adapters.includes('app') ? <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="在使用明科云APP时,页面的切换模式">
src/views/imdesign/index.jsx
New file
@@ -0,0 +1,597 @@
import React, { Component } from 'react'
import { DndProvider } from 'react-dnd'
import { withRouter } from 'react-router'
import { is, fromJS } from 'immutable'
import moment from 'moment'
import HTML5Backend from 'react-dnd-html5-backend'
import { ConfigProvider, notification, Modal, Collapse, Button, Spin } from 'antd'
import { DoubleLeftOutlined, DoubleRightOutlined, LeftOutlined, UserOutlined, EllipsisOutlined } from '@ant-design/icons'
import Api from '@/api'
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 asyncComponent from '@/utils/asyncComponent'
import getWrapForm from './options'
import './index.scss'
const { Panel } = Collapse
const { confirm } = Modal
const Header = asyncComponent(() => import('@/mob/header'))
const MenuForm = asyncComponent(() => import('./menuform'))
const CreateView = asyncComponent(() => import('@/pc/createview'))
const Transfer = asyncComponent(() => import('@/pc/transfer'))
const BgController = asyncComponent(() => import('@/pc/bgcontroller'))
const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
const NormalForm = asyncComponent(() => import('@/components/normalform'))
sessionStorage.setItem('isEditState', 'true')
sessionStorage.setItem('appType', 'mob')       // 应用类型
document.body.className = ''
window.GLOB.CacheIndependent = new Map()
class ImDesign extends Component {
  state = {
    localedict: sessionStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS,
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    loading: true,
    MenuId: '',
    MenuName: '',
    MenuNo: '',
    menuloading: false,
    oriConfig: null,
    config: null,
    direction: 'vertical',
    settingshow: true,
    controlshow: true,
    adapters: [],
  }
  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('role_type', param.role_type || 'true')
        sessionStorage.setItem('login_types', param.login_types || 'false')
        sessionStorage.setItem('typename', param.typename || 'mob')
        sessionStorage.setItem('adapter', param.adapter || '')
        sessionStorage.setItem('sysBgColor', param.sysBgColor || '#ffffff')
        sessionStorage.setItem('userbind', param.userbind || '')
        sessionStorage.setItem('instantMessage', param.instantMessage || '')
        this.setState({
          localedict: sessionStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS,
          dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
        })
        this.getAppMessage(param.MenuID)
      } else if (param.type === 'view') {
        window.GLOB.winWidth = 420
        window.GLOB.winHeight = 738
        window.GLOB.shellWidth = 376
        window.GLOB.shellHeight = 680
        if (sessionStorage.getItem('typename') === 'pad') {
          window.GLOB.winWidth = 736
          window.GLOB.winHeight = 945
          window.GLOB.shellWidth = 640
          window.GLOB.shellHeight = 853
        }
        let adapters = sessionStorage.getItem('adapter')
        if (adapters) {
          adapters = adapters.split(',')
        } else {
          adapters = []
        }
        this.setState({
          adapters,
          MenuId: param.MenuID,
        }, () => {
          this.getMenuParam()
        })
      }
    } catch (e) {
      notification.warning({
        top: 92,
        message: '菜单信息解析错误!',
        duration: 5
      })
    }
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (this.props.match.params.param !== nextProps.match.params.param) {
      window.location.reload()
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  componentDidMount () {
    setTimeout(() => {
      this.getAppPictures()
    }, 1000)
    document.onkeydown = (event) => {
      let e = event || window.event
      let keyCode = e.keyCode || e.which || e.charCode
      let preKey = ''
      if (e.ctrlKey) {
        preKey = 'ctrl'
      }
      if (e.shiftKey) {
        preKey = 'shift'
      } else if (e.altKey) {
        preKey = 'alt'
      }
      if (!preKey || !keyCode) return
      let _shortcut = `${preKey}+${keyCode}`
      if (_shortcut === 'ctrl+83') {
        let node = document.getElementById('save-config')
        if (node) {
          node.click()
        }
        return false
      }
    }
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  changeEditMenu = () => {
    const { oriConfig, config } = this.state
    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      notification.warning({
        top: 92,
        message: '配置信息未保存!',
        duration: 5
      })
      return
    }
    let param = {
      MenuID: config.wrap.linkmenu,
      copyMenuId: '',
      type: 'view'
    }
    param = window.btoa(window.encodeURIComponent(JSON.stringify(param)))
    this.props.history.push('/mobdesign/' + param)
  }
  getAppMessage = (MenuID) => {
    Api.getSystemConfig({
      func: 's_get_keyids',
      bid: sessionStorage.getItem('appId')
    }).then(res => {
      if (!res.status) {
        notification.warning({
          top: 92,
          message: res.message,
          duration: 5
        })
        return
      }
      let appViewList = []
      if (res.data && res.data.length > 0) {
        appViewList = res.data
      }
      sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
      this.props.history.replace('/imdesign/' + window.btoa(window.encodeURIComponent(JSON.stringify({MenuID: MenuID, type: 'view'}))))
    })
  }
  getAppPictures = () => {
    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
    Api.getSystemConfig({
      func: 's_url_db_adduptdel',
      PageIndex: 0,  // 0 代表全部
      PageSize: 0,   // 0 代表全部
      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 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'video',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
        }
      })
      Api.getSystemConfig({
        func: 's_url_db_adduptdel',
        PageIndex: 0,  // 0 代表全部
        PageSize: 0,   // 0 代表全部
        typecharone: 'color',
        type: 'search'
      }).then(res => {
        if (res.status) {
          sessionStorage.setItem('app_colors', JSON.stringify(res.data || []))
        }
      })
    })
  }
  closeView = () => {
    const { oriConfig, config } = this.state
    if (!config) {
      window.close()
    } else if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      confirm({
        title: '配置信息未保存,确定关闭吗?',
        content: '',
        onOk() {
          window.close()
        },
        onCancel() {}
      })
    } else {
      window.close()
    }
  }
  backView = () => {
    const { oriConfig, config } = this.state
    if (!config) {
      window.history.back()
    } else if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      confirm({
        title: '配置信息未保存,确定后退吗?',
        content: '',
        onOk() {
          window.history.back()
        },
        onCancel() {}
      })
    } else {
      window.history.back()
    }
  }
  getMenuParam = () => {
    const { MenuId } = this.state
    let param = {
      func: 'sPC_Get_LongParam',
      TypeCharOne: sessionStorage.getItem('kei_no'),
      typename: sessionStorage.getItem('typename'),
      MenuID: MenuId
    }
    Api.getSystemConfig(param).then(result => {
      if (!result.status) {
        notification.warning({
          top: 92,
          message: result.message,
          duration: 5
        })
        this.setState({loading: false})
        return
      } else {
        let config = null
        let isCreate = false
        try {
          config = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : null
        } catch (e) {
          console.warn('Parse Failure')
          config = null
        }
        if (!config) {
          isCreate = true
          config = {
            version: 1.0,
            uuid: MenuId,
            MenuID: MenuId,
            Template: 'imPage',
            enabled: false,
            MenuName: '即时通信',
            MenuNo: 'im',
            tables: [],
            components: [],
            viewType: 'im',
            wrap: {},
            style: {
              backgroundColor: '#ededed', backgroundImage: ''
            }
          }
        }
        config.uuid = MenuId
        config.MenuID = MenuId
        config.open_edition = result.open_edition || ''
        this.setState({
          oriConfig: isCreate ? null : config,
          config: fromJS(config).toJS(),
          loading: false
        })
      }
    })
    this.getAppMenus()
  }
  getAppMenus = () => {
    let _param = {
      func: 's_get_app_menus',
      TypeCharOne: sessionStorage.getItem('kei_no'),
      typename: sessionStorage.getItem('typename'),
      LText: `select '${window.GLOB.appkey}'`,
      timestamp: moment().format('YYYY-MM-DD HH:mm:ss')
    }
    _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)
    Api.getSystemConfig(_param).then(res => {
      if (!res.status) {
        notification.warning({
          top: 92,
          message: res.message,
          duration: 5
        })
        return
      }
      let appIndeList = sessionStorage.getItem('appViewList')
      appIndeList = JSON.parse(appIndeList)
      appIndeList = appIndeList.map(item => (item.keys_type !== 'index' ? item.keys_id : '')).join(',')
      if (sessionStorage.getItem('userbind')) {
        appIndeList = appIndeList + ',' + sessionStorage.getItem('userbind')
      }
      if (sessionStorage.getItem('instantMessage')) {
        appIndeList = appIndeList + ',' + sessionStorage.getItem('instantMessage')
      }
      let menus = res.menus.filter(item => appIndeList.indexOf(item.MenuID) === -1)
      menus = menus.map(item => {
        item.value = item.MenuID
        item.label = item.MenuName
        return item
      })
      sessionStorage.setItem('appMenus', JSON.stringify(menus))
    })
  }
  submitConfig = () => {
    const { adapters } = this.state
    let config = fromJS(this.state.config).toJS()
    if (!config.MenuName || !config.MenuNo) {
      notification.warning({
        top: 92,
        message: '请完善菜单基本信息!',
        duration: 5
      })
      this.setState({
        settingshow: true
      })
      return
    }
    this.setState({
      menuloading: true
    })
    setTimeout(() => {
      let roleParam = {type: 'im', key: config.uuid, title: config.MenuName, children: []}
      if (adapters.includes('wxmini')) {
        config = this.getMiniStyle(config)
      }
      let param = {
        func: 'sPC_TrdMenu_AddUpt',
        FstID: 'mk_app',
        SndID: 'mk_app',
        ParentID: 'mk_app',
        MenuID: config.uuid,
        MenuNo: config.MenuNo || '',
        EasyCode: '',
        Template: 'imPage',
        TypeCharOne: sessionStorage.getItem('kei_no'),
        Typename: sessionStorage.getItem('typename'),
        MenuName: config.MenuName || '',
        PageParam: JSON.stringify({Template: 'imPage'}),
        open_edition: config.open_edition,
        menus_rolelist: window.btoa(window.encodeURIComponent(JSON.stringify(roleParam))),
        LText: '',
        LTexttb: ''
      }
      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
      param.secretkey = Utils.encrypt('', param.timestamp)
      let _config = fromJS(config).toJS()
      param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
      Api.getSystemConfig(param).then(res => {
        if (!res.status) {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 5
          })
          this.setState({
            menuloading: false
          })
          return
        }
        config.open_edition = res.open_edition || ''
        this.setState({
          config,
          oriConfig: fromJS(config).toJS(),
          menuloading: false
        })
        notification.success({
          top: 92,
          message: '保存成功',
          duration: 2
        })
      })
    }, 300)
  }
  getWrapForms = () => {
    const { config } = this.state
    return getWrapForm(config.wrap)
  }
  updateWrap = (res) => {
    let _config = {...this.state.config, wrap: res}
    this.setState({
      config: _config
    })
  }
  // 更新配置信息
  updateConfig = (config) => {
    this.setState({
      config: config
    })
  }
  render () {
    const { localedict, loading, settingshow, controlshow, dict, MenuId, config, menuloading, adapters } = this.state
    return (
      <ConfigProvider locale={localedict}>
        <div className="mk-mob-view" id="mk-mob-design-view">
          <Header/>
          {loading ? <Spin className="view-spin" size="large" /> : null}
          <DndProvider backend={HTML5Backend}>
            <div className={'menu-setting ' + (!settingshow ? 'hidden' : '')}>
              <div className="draw">
                {settingshow ? <DoubleLeftOutlined onClick={() => {this.setState({settingshow: false})}} /> : null}
                {!settingshow ? <DoubleRightOutlined onClick={() => {this.setState({settingshow: true})}} /> : null}
              </div>
              <div className="pc-setting-tools">
                <Collapse accordion defaultActiveKey="basedata" bordered={false}>
                  {/* 基本信息 */}
                  <Panel header={dict['mob.basemsg']} forceRender key="basedata">
                    {/* 菜单信息 */}
                    {config ? <MenuForm
                      dict={dict}
                      config={config}
                      MenuId={MenuId}
                      adapters={adapters}
                      updateConfig={this.updateConfig}
                    /> : null}
                  </Panel>
                  <Panel header={'页面样式'} key="background">
                    {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
                  </Panel>
                </Collapse>
              </div>
            </div>
            <div className={'menu-control ' + (!controlshow ? 'hidden' : '')}>
              <div className="draw">
                {controlshow ? <DoubleRightOutlined onClick={() => {this.setState({controlshow: false})}}/> : null}
                {!controlshow ? <DoubleLeftOutlined onClick={() => {this.setState({controlshow: true})}}/> : null}
              </div>
              <div className="wrap">
                <Button type="primary" onClick={this.submitConfig} id="save-config" loading={menuloading}>{dict['mob.save']}</Button>
                <NormalForm title="即时通信设置" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
                  <Button type="default" style={{borderColor: 'rgb(64, 169, 255)', color: 'rgb(64, 169, 255)'}}>设置</Button>
                </NormalForm>
                <Button type="default" className="mk-border-purple" onClick={this.backView}>后退</Button>
                <CreateView resetmenu={this.getAppMenus} />
                <Transfer MenuID={MenuId} />
                <Button type="default" onClick={this.closeView}>关闭</Button>
              </div>
            </div>
            <div className={'menu-body menu-view'}>
              <div className="mob-shell" style={{width: window.GLOB.shellWidth, height: window.GLOB.shellHeight}}>
                <div className="instant-message" style={config ? config.style : null}>
                  <div className="header">
                    <LeftOutlined/>
                    <span className="title">friend</span>
                    {config && config.wrap.linkmenu ? <EllipsisOutlined onClick={this.changeEditMenu}/> : null}
                  </div>
                  <div className="mk-content-wrap">
                    <div className="line-wrap">
                      <div className="time-line">12:34</div>
                      <div className="line-msg">
                        <div className="portrait">
                          <div className="img"><UserOutlined /></div>
                        </div>
                        <div className="msg">
                          <div className="title">friend</div>
                          <div className="words">您好</div>
                        </div>
                      </div>
                    </div>
                    <div className="line-wrap">
                      <div className="time-line">12:45</div>
                      <div className="line-msg right">
                        <div className="msg">
                          <div className="words">
                            您好
                          </div>
                        </div>
                        <div className="portrait">
                          <div className="img"><UserOutlined /></div>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div className="send-wrap">
                    <div className="adm-input"></div>
                    <div className="send"><Button>发送</Button></div>
                  </div>
                </div>
              </div>
            </div>
          </DndProvider>
          <StyleController />
        </div>
      </ConfigProvider>
    )
  }
}
export default withRouter(ImDesign)
src/views/imdesign/index.scss
New file
@@ -0,0 +1,341 @@
.mk-mob-view {
  min-height: 100vh;
  .change-view {
    display: none;
  }
  >.view-spin {
    position: absolute;
    z-index: 3;
    left: calc(50% - 16px);
    top: calc(50vh - 70px);
  }
  .modal-form-board {
    padding-top: 0;
  }
  .menu-setting {
    position: fixed;
    left: 0;
    top: 48px;
    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;
      .anticon {
        padding: 12px 3px;
      }
    }
    .pc-setting-tools {
      height: calc(100vh - 48px);
      width: 300px;
      background: #ffffff;
      overflow-y: auto;
      overflow-x: hidden;
      padding-bottom: 50px;
      .normal-view {
        display: none;
      }
      > .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;
          }
        }
      }
    }
  }
  .menu-setting.hidden {
    left: -300px;
  }
  .mob-shell {
    margin: 0 auto;
    background: #000000;
    background-size: 100% 100%;
    padding: 25px 13px 40px;
    border-radius: 30px;
  }
  .menu-control {
    position: fixed;
    right: 0;
    top: 48px;
    height: calc(100vh - 48px);
    background: #ffffff;
    z-index: 10;
    transition: right 0.3s;
    width: 145px;
    .draw {
      position: absolute;
      z-index: 1;
      background: #ffffff;
      left: -21px;
      top: 0px;
      box-shadow: 0 0 1px #959595;
      border-radius: 0 2px 2px 0px;
      .anticon {
        padding: 12px 3px;
      }
    }
    div:not(.draw), button:not(.ant-switch) {
      display: block!important;
      margin-bottom: 15px;
      width: 100%;
    }
    .wrap {
      height: 100%;
      padding: 20px 10px;
    }
  }
  .menu-control.hidden {
    right: -145px;
  }
  .menu-body {
    width: 100vw;
    height: 100vh;
    overflow-x: hidden;
    position: relative;
    background: #959595;
    padding: 50px 0px 0px;
    overflow-y: auto;
  }
  .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);
  }
  .instant-message {
    width: 100%;
    height: 100%;
    background-color: #ededed;
    padding-top: 40px;
    padding-bottom: 50px;
    background-position: center;
    position: relative;
    .header {
      position: absolute;
      top: 0px;
      width: 100%;
      height: 40px;
      line-height: 40px;
      background: #ffffff;
      box-shadow: 0 0 3px #d9d9d9;
      text-align: center;
      font-size: 16px;
      z-index: 1;
      .anticon-left {
        position: absolute;
        left: 0px;
        padding: 10px 13px;
        font-size: 18px;
      }
    }
    .mk-content-wrap {
      height: calc(100% - 90px);
      overflow-y: auto;
      padding-bottom: 10px;
      padding-top: 5px;
    }
    .mk-content-wrap.friend {
      .line-wrap:not(.right) {
        .title {
          display: none;
        }
        .words::after {
          top: 10px;
        }
      }
    }
    .line-wrap {
      margin-bottom: 10px;
      .time-line {
        text-align: center;
        margin-bottom: 10px;
        color: #757575;
        font-size: 12px;
      }
      .line-msg {
        display: flex;
        width: calc(100% - 50px);
        .portrait {
          width: 50px;
          text-align: center;
          .img {
            width: 32px;
            height: 32px;
            background-color: #bcbcbc;
            display: inline-block;
            background-repeat: no-repeat;
            background-size: cover;
            background-position: center;
            line-height: 32px;
            font-size: 16px;
          }
        }
        .msg {
          flex: 1;
          .title {
            font-size: 12px;
            color: rgba(0,0,0,0.35);
            margin-bottom: 3px;
            margin-top: -5px;
          }
          .words {
            position: relative;
            display: inline-block;
            padding: 5px 10px;
            background: #ffffff;
            border-radius: 3px;
            min-height: 32px;
            font-size: 14px;
            text-align: left;
          }
          .words::after {
            position: absolute;
            content: ' ';
            display: block;
            width: 10px;
            height: 10px;
            transform: rotate(45deg);
            background: #ffffff;
            top: 4px;
            left: -2px;
          }
        }
      }
    }
    .line-msg.right {
      margin-left: 50px;
      .msg {
        text-align: right;
        .words {
          background: #93ED6C;
          color: #000000;
        }
        .words::after {
          background: #93ED6C;
          top: 10px;
          left: auto;
          right: -2px;
        }
      }
    }
    .send-wrap {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      background-color: #f7f7f7;
      border-top: 1px solid #e9e9e9;
      height: 50px;
      line-height: 50px;
      display: flex;
      .adm-input {
        padding: 4px 10px;
        background: #ffffff;
        margin: 7px 10px;
        flex: 1;
      }
      .send {
        width: 75px;
      }
      .ant-btn {
        padding: 2px 15px;
        height: 32px;
        margin: 9px 5px 0px 0px;
        border-color: #26C281;
        background-color: #26C281;
        color: #ffffff;
      }
    }
  }
}
body {
  overflow-y: hidden;
}
src/views/imdesign/menuform/index.jsx
New file
@@ -0,0 +1,77 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input } from 'antd'
import './index.scss'
class CustomMenuForm extends Component {
  static propTpyes = {
    dict: PropTypes.object, // 字典项
    config: PropTypes.object,
    MenuId: PropTypes.string,
    adapters: PropTypes.array,
    updateConfig: PropTypes.func
  }
  state = {}
  // 菜单名称
  changeName = (e) => {
    this.props.updateConfig({...this.props.config, MenuName: e.target.value})
  }
  // 菜单参数
  changeNo = (e) => {
    this.props.updateConfig({...this.props.config, MenuNo: e.target.value})
  }
  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>
        </Row>
      </Form>
    )
  }
}
export default Form.create()(CustomMenuForm)
src/views/imdesign/menuform/index.scss
New file
@@ -0,0 +1,25 @@
.custom-menu-form {
  .ant-form-item {
    height: 50px;
    .ant-form-explain {
      font-size: 12px;
    }
  }
  .ant-radio-group {
    white-space: nowrap;
  }
  .mk-source-wrap .mk-source-item-info .anticon-delete {
    color: #ff4d4f;
  }
  .status-bar {
    .ant-form-item-control {
      padding-top: 7px;
      .color-sketch-block-box {
        width: calc(100% - 140px);
      }
      .color-sketch-value {
        width: 140px;
      }
    }
  }
}
src/views/imdesign/options.jsx
New file
@@ -0,0 +1,29 @@
/**
 * @description Wrap表单配置信息
 */
export default function (wrap) {
  let menulist = sessionStorage.getItem('appMenus')
  if (menulist) {
    try {
      menulist = JSON.parse(menulist)
    } catch (e) {
      menulist = []
    }
  } else {
    menulist = []
  }
  const WrapForm = [
    {
      type: 'select',
      field: 'linkmenu',
      label: '关联菜单',
      initval: wrap.linkmenu || '',
      required: false,
      options: menulist,
    },
  ]
  return WrapForm
}
src/views/mobdesign/index.jsx
@@ -17,6 +17,7 @@
import MKEmitter from '@/utils/events.js'
import MenuUtils from '@/utils/utils-custom.js'
import asyncComponent from '@/utils/asyncComponent'
import backurl from '@/assets/img/back.jpg'
import './index.scss'
@@ -72,7 +73,8 @@
    settingshow: true,
    controlshow: true,
    comloading: false,
    adapters: []
    adapters: [],
    viewType: 'menu'
  }
  UNSAFE_componentWillMount() {
@@ -89,6 +91,8 @@
        sessionStorage.setItem('typename', param.typename || 'mob')
        sessionStorage.setItem('adapter', param.adapter || '')
        sessionStorage.setItem('sysBgColor', param.sysBgColor || '#ffffff')
        sessionStorage.setItem('userbind', param.userbind || '')
        sessionStorage.setItem('instantMessage', param.instantMessage || '')
        this.setState({
          localedict: sessionStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS,
@@ -117,7 +121,8 @@
        this.setState({
          adapters,
          MenuId: param.MenuID
          MenuId: param.MenuID,
          viewType: /^userbind/.test(param.MenuID) ? 'userbind' : 'menu'
        }, () => {
          this.getMenuParam(param)
        })
@@ -444,10 +449,7 @@
    if (!config) {
      window.close()
      return
    }
    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
    } else if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      confirm({
        title: '配置信息未保存,确定关闭吗?',
        content: '',
@@ -458,6 +460,25 @@
      })
    } else {
      window.close()
    }
  }
  backView = () => {
    const { oriConfig, config } = this.state
    if (!config) {
      window.history.back()
    } else if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      confirm({
        title: '配置信息未保存,确定后退吗?',
        content: '',
        onOk() {
          window.history.back()
        },
        onCancel() {}
      })
    } else {
      window.history.back()
    }
  }
@@ -491,6 +512,11 @@
        } catch (e) {
          console.warn('Parse Failure')
          config = null
        }
        if (/^userbind/.test(MenuId)) {
          this.setUserBindMenu(config, result)
          return
        }
        if (!config) {
@@ -541,6 +567,88 @@
    this.getAppMenus()
  }
  setUserBindMenu = (config, result) => {
    const { MenuId } = this.state
    let isCreate = !config
    if (!config) {
      config = {
        version: 1.0,
        uuid: MenuId,
        MenuID: MenuId,
        Template: 'webPage',
        enabled: false,
        MenuName: '用户绑定',
        MenuNo: 'user_bind',
        tables: [],
        components: [],
        viewType: 'userbind',
        statusBarbgColor: sessionStorage.getItem('sysBgColor') || '#ffffff',
        style: {
          backgroundColor: sessionStorage.getItem('sysBgColor') || '#ffffff', backgroundImage: ''
        }
      }
      config.components.push({
        subcards:[
          {
            uuid: Utils.getuuid(), setting: {width:24, primaryId: '', click: ''}, style: {}, backStyle: {},
            elements: [{maxWidth: 135, datatype: 'static', width: 24, marks: null, url: 'http://cloud.mk9h.cn/Content/images/20220120/cb5cd13a-2ef4-41cc-ab6a-a202f1c04da5.png', style: {borderRadius: '50%'}, eleType: 'picture', link: '', uuid: Utils.getuuid(), lenWidRadio: '1:1', $type: ''}],
            backElements: []
          }
        ],
        headerStyle: {}, parentId: '', width: 24, scripts: [], btnlog: [], pageable: false,
        wrap: {name: 'logo', width: 24, datatype: 'static', cardType: '', blacklist: []},
        name: 'logo', floor: 1, switchable: true,
        setting:{interType: 'system'}, tabId: '', style:{paddingTop: '10vh', paddingBottom: '10vh'}, dataName: Utils.getuuid(), format: 'object', subtype: 'propcard', type: 'card', uuid: Utils.getuuid(), columns: []
      })
      config.components.push({
        uuid: Utils.getuuid(),
        type: 'login',
        floor: 1,
        tabId: '',
        parentId: '',
        dataName: '',
        width: 24,
        name: '用户绑定',
        subtype: 'bindlogin',
        wrap: { name: '用户绑定', width: 24, loginWays: ['uname_pwd'], shortcut: 'none', height: '45vh' },
        style: {},
        loginWays: [
          {type: 'uname_pwd', label: '账号登录', shortcut: 'none'},
          {type: 'sms_vcode', label: '短信登录'},
        ]
      })
      config.components.push({
        subcards:[
          {
            uuid: Utils.getuuid(), setting: {width:24, primaryId: '', click: ''}, style: {}, backStyle: {},
            elements: [{datatype: 'static', width: 24, marks: null, height: null, value: 'Power by Minkesoft', style: { fontSize: '13px', textAlign: 'center'}, eleType: 'text', link: '', uuid: Utils.getuuid(), $type: ''}],
            backElements: []
          }
        ],
        headerStyle: {}, parentId: '', width: 24, scripts: [], btnlog: [], pageable: false,
        wrap: {name: 'Power', width: 24, datatype: 'static', cardType: '', blacklist: []},
        name: 'Power', floor: 1, switchable: true,
        setting:{interType: 'system'}, tabId: '', style:{}, dataName: Utils.getuuid(), format: 'object', subtype: 'propcard', type: 'card', uuid: Utils.getuuid(), columns: []
      })
    }
    config.uuid = MenuId
    config.MenuID = MenuId
    config.open_edition = result.open_edition || ''
    this.setState({
      oriConfig: isCreate ? null : config,
      config: fromJS(config).toJS(),
      activeKey: isCreate ? 'basedata' : 'component',
      loading: false
    })
    window.GLOB.customMenu = config
  }
  getAppMenus = () => {
    let _param = {
      func: 's_get_app_menus',
@@ -565,6 +673,13 @@
      let appIndeList = sessionStorage.getItem('appViewList')
      appIndeList = JSON.parse(appIndeList)
      appIndeList = appIndeList.map(item => (item.keys_type !== 'index' ? item.keys_id : '')).join(',')
      if (sessionStorage.getItem('userbind')) {
        appIndeList = appIndeList + ',' + sessionStorage.getItem('userbind')
      }
      if (sessionStorage.getItem('instantMessage')) {
        appIndeList = appIndeList + ',' + sessionStorage.getItem('instantMessage')
      }
      let menus = res.menus.filter(item => appIndeList.indexOf(item.MenuID) === -1)
      menus = menus.map(item => {
@@ -1247,7 +1362,7 @@
  }
  verifyConfig = (show) => {
    const { config } = this.state
    const { config, viewType } = this.state
    let error = ''
    let searchSum = 0
    let swipes = []
@@ -1313,6 +1428,10 @@
    }
    check(config.components)
    if (!error && viewType === 'userbind' && config.components.filter(item => item.type === 'login').length === 0) {
      error = '用户绑定页面必须添加登录。'
    }
    if (!error && searchSum > 1) {
      error = '搜索组件与导航栏的搜索功能不可同时使用。'
@@ -1540,11 +1659,11 @@
  render () {
    const { localedict, comloading, loading, settingshow, controlshow, activeKey, dict, MenuId, config, menuloading, customComponents, adapters } = this.state
    const { viewType, localedict, comloading, loading, settingshow, controlshow, activeKey, dict, MenuId, config, menuloading, customComponents, adapters } = this.state
    return (
      <ConfigProvider locale={localedict}>
        <div className="mk-mob-view" id="mk-mob-design-view">
        <div className={'mk-mob-view ' + viewType} id="mk-mob-design-view">
          <Header changeView={this.changeView}/>
          {loading ? <Spin className="view-spin" size="large" /> : null}
          <DndProvider backend={HTML5Backend}>
@@ -1556,7 +1675,7 @@
              <div className="pc-setting-tools">
                <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
                  {/* 基本信息 */}
                  <Panel header={dict['mob.basemsg']} forceRender key="basedata">
                  <Panel header={dict['mob.basemsg']} forceRender className="basedata" key="basedata">
                    {/* 菜单信息 */}
                    {config ? <MenuForm
                      dict={dict}
@@ -1571,10 +1690,10 @@
                    {config ? <Paragraph style={{padding: '15px 0px 0px 18px'}} copyable={{ text: MenuId }}>菜单ID</Paragraph> : null}
                  </Panel>
                  {/* 组件添加 */}
                  <Panel header={dict['mob.component']} key="component">
                  <Panel header={dict['mob.component']} className="component" key="component">
                    <SourceWrap />
                  </Panel>
                  {customComponents && customComponents.length ? <Panel header="自定义组件" key="cuscomponent">
                  {customComponents && customComponents.length ? <Panel header="自定义组件" className="cuscomponent" key="cuscomponent">
                    <SourceWrap components={customComponents} />
                  </Panel> : null}
                  <Panel header={'页面样式'} key="background">
@@ -1591,14 +1710,15 @@
              <div className="wrap">
                <Button type="primary" onClick={this.submitConfig} id="save-config" loading={menuloading}>{dict['mob.save']}</Button>
                <Switch className="big" checkedChildren={dict['mob.enable']} unCheckedChildren={dict['mob.disable']} checked={config && config.enabled} onChange={this.onEnabledChange} />
                <img title="后退" className="back-view" onClick={this.backView} src={backurl} alt=""/>
                <CreateView resetmenu={this.getAppMenus} />
                <PasteController insert={this.insert} />
                <StyleCombControlButton menu={config} />
                <SysInterface config={config} updateConfig={this.updateConfig}/>
                <PictureController/>
                <Quotecomponent config={config} updateConfig={this.updateConfig}/>
                <Button className="mk-border-green" onClick={this.setHomeView}><HomeOutlined /> 设为首页</Button>
                <Button className="mk-border-purple" onClick={this.setLoginView}><LoginOutlined /> 设为登录页</Button>
                <Button className="mk-border-green set-home" onClick={this.setHomeView}><HomeOutlined /> 设为首页</Button>
                <Button className="mk-border-purple set-login" onClick={this.setLoginView}><LoginOutlined /> 设为登录页</Button>
                <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
                <Transfer MenuID={MenuId} />
                <Versions MenuId={MenuId} open_edition={config ? config.open_edition : ''}/>
src/views/mobdesign/index.scss
@@ -7,6 +7,17 @@
    left: calc(50% - 16px);
    top: calc(50vh - 70px);
  }
  .back-view {
    width: 28px;
    float: right;
    margin-right: 10px;
    cursor: pointer;
    filter: opacity(0.7);
    transition: filter 0.3s;
  }
  .back-view:hover {
    filter: opacity(0.9);
  }
  .modal-form-board {
    padding-top: 0;
  }
@@ -243,6 +254,41 @@
  }
}
.mk-mob-view.userbind {
  .pc-setting-tools {
    .ant-collapse-item.basedata {
      .custom-menu-form > .ant-row {
        > .ant-col {
          display: none;
        }
        > .ant-col:nth-child(1), > .ant-col:nth-child(2) {
          display: block;
        }
      }
      .url-field-component, .model-table-tablemanage-view, .ant-typography {
        display: none;
      }
    }
    .ant-collapse-item.component {
      .menu-source-item {
        display: none;
      }
      .menu-source-item.card-propcard, .menu-source-item.balcony-balcony, .menu-source-item.login-normallogin {
        display: block;
      }
    }
    .ant-collapse-item.cuscomponent {
      display: none;
    }
  }
  .menu-control {
    .back-view, .mk-view-paste, .quote-wrap, .mk-sys-interface, .set-home, .set-login, .mk-replace-field {
      display: none!important;
    }
  }
}
body {
  overflow-y: hidden;
}
src/views/pcdesign/index.jsx
@@ -17,6 +17,7 @@
import MKEmitter from '@/utils/events.js'
import MenuUtils from '@/utils/utils-custom.js'
import asyncComponent from '@/utils/asyncComponent'
import backurl from '@/assets/img/back.jpg'
import './index.scss'
@@ -546,10 +547,7 @@
    if (!config) {
      window.close()
      return
    }
    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
    } else if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      confirm({
        title: '配置信息未保存,确定关闭吗?',
        content: '',
@@ -560,6 +558,25 @@
      })
    } else {
      window.close()
    }
  }
  backView = () => {
    const { oriConfig, config } = this.state
    if (!config) {
      window.history.back()
    } else if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
      confirm({
        title: '配置信息未保存,确定后退吗?',
        content: '',
        onOk() {
          window.history.back()
        },
        onCancel() {}
      })
    } else {
      window.history.back()
    }
  }
@@ -1761,6 +1778,7 @@
              <div className="wrap">
                <Button type="primary" id="save-config" 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} />
                <img title="后退" className="back-view" onClick={this.backView} src={backurl} alt=""/>
                <CreateView resetmenu={this.getAppMenus} />
                <PasteController insert={this.insert} />
                <StyleCombControlButton menu={config} />
src/views/pcdesign/index.scss
@@ -11,6 +11,17 @@
    left: calc(50% - 16px);
    top: calc(50vh - 70px);
  }
  .back-view {
    width: 28px;
    float: right;
    margin-right: 10px;
    cursor: pointer;
    filter: opacity(0.7);
    transition: filter 0.3s;
  }
  .back-view:hover {
    filter: opacity(0.9);
  }
  .modal-form-board {
    padding-top: 0;
  }
src/views/rolemanage/index.jsx
@@ -34,7 +34,15 @@
        align: 'center',
        render: (text, record) => (
          <div>
            <Button type="link" onClick={() => this.deleteMenu(record)} style={{color: '#ff4d4f'}}>删除</Button>
            {record.type !== 'none' ?
              <Button type="link" onClick={() => this.deleteMenu(record)} style={{color: '#ff4d4f'}}>删除</Button> :
              <Button type="link" onClick={() => {
                notification.warning({
                  top: 92,
                  message: '当前系统菜单尚未创建。',
                  duration: 5
                })
              }} style={{color: '#ff4d4f', opacity: '0.5'}}>删除</Button>}
            <Button type="link" onClick={() => this.jumpApp(record)} style={{color: '#1890ff', marginLeft: '5px'}}>编辑</Button>
          </div>
        ),
@@ -112,26 +120,48 @@
    Api.getCloudConfig(param).then(result => {
      if (result.status) {
        this.setState({
          menulist: result.menus.map(item => {
            item.nodes = ''
            item.type = 'view'
            if (item.menus_rolelist) {
              try {
                let pageParam = JSON.parse(window.decodeURIComponent(window.atob(item.menus_rolelist)))
                item.nodes = pageParam
                if (pageParam.login) {
                  item.nodes = ''
                } else if (pageParam.type === 'navbar') {
                  item.type = 'navbar'
                }
              } catch (e) {
        let ub = app.user_binding === 'true' && app.userbind ? false : true
        let im = app.instantMessage ? false : true
        let menus = result.menus.map(item => {
          item.nodes = ''
          item.type = 'view'
          if (item.menus_rolelist) {
            try {
              let pageParam = JSON.parse(window.decodeURIComponent(window.atob(item.menus_rolelist)))
              item.nodes = pageParam
              if (pageParam.login) {
                item.nodes = ''
              } else if (pageParam.type === 'navbar') {
                item.type = 'navbar'
              } else if (pageParam.type === 'im') {
                item.type = 'im'
                item.nodes = ''
              }
            } catch (e) {
              item.nodes = ''
            }
            return item
          })
          }
          if (!ub && app.userbind === item.MenuID) {
            ub = true
          }
          if (!im && app.instantMessage === item.MenuID) {
            im = true
          }
          return item
        })
        if (!im) {
          menus.push({nodes: '', type: 'none', MenuID: app.instantMessage, MenuName: '即时通信'})
        }
        if (!ub) {
          menus.push({nodes: '', type: 'none', MenuID: app.userbind, MenuName: '用户绑定'})
        }
        this.setState({
          menulist: menus
        }, () => {
          if (reset && (!this.oriTrees || this.oriTrees.length === 0)) {
            this.initMenutree()
@@ -818,6 +848,10 @@
      return
    }
    if (app.instantMessage && item.MenuID === app.instantMessage) {
      route = 'imdesign'
    }
    window.open(window.location.href.replace(/#.+/ig, `#/${route}/${window.btoa(window.encodeURIComponent(JSON.stringify({...app, MenuID: item.MenuID, type: 'app'})))}`))
  }