king
2021-07-28 2e3d8e7d5715862733e43070e7df73b48a81948f
2021-07-28
36个文件已修改
6个文件已添加
1865 ■■■■ 已修改文件
src/menu/components/card/balcony/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/action.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/elementform/index.jsx 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/formconfig.jsx 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/prop-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/table-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/cardcomponent/index.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/data-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/prop-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-dashboard/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-scatter/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/code/sandbox/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/normal-form/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/normal-group/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/dragaction/card.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/antv-tabs/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/picturecontroller/editform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/index.jsx 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/menucomponent/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/index.jsx 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/tabs/antv-tabs/dragabletabs.jsx 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/tabs/antv-tabs/index.jsx 408 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/tabs/antv-tabs/index.scss 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/tabs/tabcomponents/card.jsx 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/tabs/tabcomponents/index.jsx 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/tabs/tabcomponents/index.scss 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobshell/card.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobshell/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/menushell/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/rolemanage/index.jsx 670 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/rolemanage/index.scss 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/balcony/index.jsx
@@ -128,7 +128,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/card/cardcellcomponent/dragaction/action.jsx
@@ -41,9 +41,9 @@
  if (card.show === 'icon') {
    btnElement = (<Button style={_style} type="link"><Icon type={card.icon}/></Button>)
  } else if (card.show === 'link') {
    btnElement = (<Button style={_style} type="link">{card.icon ? <Icon type={card.icon}/> : null}{card.label}</Button>)
    btnElement = (<Button style={_style} type="link">{card.label}{card.icon ? <Icon type={card.icon}/> : null}</Button>)
  } else {
    btnElement = (<Button style={_style}> {card.label}{card.icon ? <Icon type={card.icon}/> : null} </Button>)
    btnElement = (<Button style={_style}> {card.icon ? <Icon type={card.icon}/> : null}{card.label} </Button>)
  }
  return (
src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -10,6 +10,7 @@
const { TextArea } = Input
const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
const MkIcon = asyncComponent(() => import('@/components/mkIcon'))
const cardTypeOptions = {
  sequence: ['eleType', 'width'],
@@ -353,6 +354,27 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'icon') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal || '',
                rules: [{
                  required: !!item.required,
                  message: this.props.dict['form.required.select'] + item.label + '!'
                }]
              })(
                <MkIcon />
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'radio') {
        fields.push(
          <Col span={12} key={index}>
src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -63,45 +63,11 @@
      options: _options
    },
    {
      type: 'select',
      type: 'icon',
      key: 'icon',
      label: '图标',
      initVal: card.icon,
      required: true,
      options: [
        { value: 'question-circle', text: 'question-circle'},
        { value: 'alert', text: 'alert'},
        { value: 'cloud', text: 'cloud'},
        { value: 'eye', text: 'eye'},
        { value: 'eye-invisible', text: 'eye-invisible'},
        { value: 'android', text: 'android'},
        { value: 'apple', text: 'apple'},
        { value: 'windows', text: 'windows'},
        { value: 'ie', text: 'ie'},
        { value: 'chrome', text: 'chrome'},
        { value: 'github', text: 'github'},
        { value: 'aliwangwang', text: 'aliwangwang'},
        { value: 'dingding', text: 'dingding'},
        { value: 'wechat', text: 'wechat'},
        { value: 'alipay', text: 'alipay'},
        { value: 'weibo-square', text: 'weibo-square'},
        { value: 'weibo-circle', text: 'weibo-circle'},
        { value: 'taobao-circle', text: 'taobao-circle'},
        { value: 'weibo', text: 'weibo'},
        { value: 'twitter', text: 'twitter'},
        { value: 'youtube', text: 'youtube'},
        { value: 'alipay-circle', text: 'alipay-circle'},
        { value: 'taobao', text: 'taobao'},
        { value: 'skype', text: 'skype'},
        { value: 'qq', text: 'qq'},
        { value: 'gitlab', text: 'gitlab'},
        { value: 'zhihu', text: 'zhihu'},
        { value: 'slack', text: 'slack'},
        { value: 'sketch', text: 'sketch'},
        { value: 'yahoo', text: 'yahoo'},
        { value: 'reddit', text: 'reddit'},
        { value: 'dribbble', text: 'dribbble'},
      ]
      required: true
    },
    {
      type: 'radio',
src/menu/components/card/cardcomponent/index.jsx
@@ -235,10 +235,6 @@
    let _style = {...card.style}
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    if (side === 'back') {
      _style = {
        ...card.backStyle,
src/menu/components/card/data-card/index.jsx
@@ -236,7 +236,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/card/prop-card/index.jsx
@@ -238,7 +238,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/card/table-card/index.jsx
@@ -210,7 +210,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/carousel/cardcomponent/index.jsx
@@ -172,9 +172,6 @@
    let _style = {...card.style}
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    _style.height = cards.style.height
    _style = resetStyle(_style)
src/menu/components/carousel/data-card/index.jsx
@@ -201,7 +201,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/carousel/prop-card/index.jsx
@@ -218,7 +218,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/chart/antv-bar/index.jsx
@@ -1293,7 +1293,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/chart/antv-dashboard/index.jsx
@@ -489,7 +489,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/chart/antv-pie/index.jsx
@@ -651,7 +651,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/chart/antv-scatter/index.jsx
@@ -324,7 +324,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/code/sandbox/index.jsx
@@ -116,7 +116,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/editor/braft-editor/index.jsx
@@ -115,7 +115,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/form/normal-form/index.jsx
@@ -185,7 +185,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/group/normal-group/index.jsx
@@ -101,7 +101,7 @@
  changeStyle = () => {
    const { group } = this.state
    MKEmitter.emit('changeStyle', [group.uuid], ['background', 'border', 'padding', 'margin'], group.style)
    MKEmitter.emit('changeStyle', [group.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], group.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/search/main-search/index.jsx
@@ -107,7 +107,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  /**
src/menu/components/share/actioncomponent/dragaction/card.jsx
@@ -38,6 +38,7 @@
  let btnElement = null
  let _style = resetStyle(card.style)
  if (card.show === 'icon') {
    btnElement = (
      <Button
src/menu/components/table/normal-table/index.jsx
@@ -221,7 +221,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/tabs/antv-tabs/index.jsx
@@ -130,7 +130,7 @@
  changeStyle = () => {
    const { tabs } = this.state
    MKEmitter.emit('changeStyle', [tabs.uuid], ['background', 'border', 'padding', 'margin'], tabs.style)
    MKEmitter.emit('changeStyle', [tabs.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], tabs.style)
  }
  getStyle = (comIds, style) => {
src/menu/components/tree/antd-tree/index.jsx
@@ -114,7 +114,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
src/menu/picturecontroller/editform/index.jsx
@@ -82,7 +82,7 @@
              })(
                <FileUpload config={{
                  initval: '',
                  suffix: '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp',
                  suffix: '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp,.ico',
                  maxfile: 1,
                  fileType: 'picture'
                }} />
src/menu/stylecontroller/index.jsx
@@ -218,7 +218,44 @@
   * @description 修改阴影颜色 ,颜色控件
   */
  changeShadowColor = (val) => {
    this.updateStyle({shadow: val})
    const { card } = this.state
    let boxShadow = `${card.hShadow || '0px'} ${card.vShadow || '0px'} ${card.shadowBlur || '0px'} ${val}`
    this.updateStyle({shadowColor: val, boxShadow})
  }
  /**
   * @description 修改阴影颜色 ,颜色控件
   */
  changeShadowBlur = (val) => {
    const { card } = this.state
    let boxShadow = `${card.hShadow || '0px'} ${card.vShadow || '0px'} ${val || '0px'} ${card.shadowColor || 'transparent'}`
    this.updateStyle({shadowBlur: val, boxShadow})
  }
  /**
   * @description 修改阴影颜色 ,颜色控件
   */
  changeHShadow = (val) => {
    const { card } = this.state
    let boxShadow = `${val || '0px'} ${card.vShadow || '0px'} ${card.shadowBlur || '0px'} ${card.shadowColor || 'transparent'}`
    this.updateStyle({hShadow: val, boxShadow})
  }
  /**
   * @description 修改阴影颜色 ,颜色控件
   */
  changeVShadow = (val) => {
    const { card } = this.state
    let boxShadow = `${card.hShadow || '0px'} ${val || '0px'} ${card.shadowBlur || '0px'} ${card.shadowColor || 'transparent'}`
    this.updateStyle({vShadow: val, boxShadow})
  }
  imgChange = (val) => {
@@ -609,7 +646,34 @@
                    label={<Icon title="阴影颜色" type="bg-colors" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <ColorSketch value={card.shadow || 'transparent'} onChange={this.changeShadowColor} />
                    <ColorSketch value={card.shadowColor || 'transparent'} onChange={this.changeShadowColor} />
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="模糊距离" type="column-width" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.shadowBlur || '0px'} options={['px']} onChange={this.changeShadowBlur}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="水平位置" type="arrow-right" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.hShadow || '0px'} options={['px']} onChange={this.changeHShadow}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="垂直位置" type="arrow-down" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.vShadow || '0px'} options={['px']} onChange={this.changeVShadow}/>
                  </Form.Item>
                </Col>
              </Panel> : null}
src/mob/components/menubar/normal-menubar/index.jsx
@@ -155,7 +155,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
  }
  getStyle = (comIds, style) => {
@@ -242,7 +242,7 @@
    return (
      <div className="menu-menubar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <NormalHeader config={card} updateComponent={this.updateComponent}/>
        {card.wrap.title ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null}
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加菜单" onClick={this.addMenu} type="plus" />
src/mob/components/menubar/normal-menubar/menucomponent/index.jsx
@@ -153,10 +153,6 @@
    let _style = {...card.style}
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    _style = resetStyle(_style)
    return (
src/mob/components/navbar/normal-navbar/index.jsx
@@ -118,7 +118,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['font', 'background', 'border', 'padding'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['font', 'background', 'border', 'padding', 'shadow'], card.style)
  }
  clickComponent = (e) => {
@@ -145,9 +145,6 @@
    const { card } = this.state
    let _style = {...card.style}
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    _style.height = card.wrap.height
    return (
src/mob/components/tabs/antv-tabs/dragabletabs.jsx
New file
@@ -0,0 +1,129 @@
import React, { Component } from 'react'
import { Tabs } from 'antd'
import { is, fromJS } from 'immutable'
import { DndProvider, DragSource, DropTarget } from 'react-dnd'
// Drag & Drop node
class TabNode extends Component {
  render() {
    const { connectDragSource, connectDropTarget, children } = this.props
    return connectDragSource(connectDropTarget(children))
  }
}
const cardTarget = {
  drop(props, monitor) {
    const dragKey = monitor.getItem().index
    const hoverKey = props.index
    if (dragKey === hoverKey) {
      return;
    }
    props.moveTabNode(dragKey, hoverKey)
    monitor.getItem().index = hoverKey
  }
}
const cardSource = {
  beginDrag(props) {
    return {
      id: props.id,
      index: props.index,
    }
  }
}
const WrapTabNode = DropTarget('DND_NODE', cardTarget, connect => ({
  connectDropTarget: connect.dropTarget(),
}))(
  DragSource('DND_NODE', cardSource, (connect, monitor) => ({
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging(),
  }))(TabNode)
)
class DraggableTabs extends Component {
  state = {
    order: []
  }
  moveTabNode = (dragKey, hoverKey) => {
    const newOrder = this.state.order.slice()
    const { children } = this.props
    React.Children.forEach(children, c => {
      if (newOrder.indexOf(c.key) === -1) {
        newOrder.push(c.key)
      }
    })
    const dragIndex = newOrder.indexOf(dragKey)
    const hoverIndex = newOrder.indexOf(hoverKey)
    newOrder.splice(dragIndex, 1)
    newOrder.splice(hoverIndex, 0, dragKey)
    this.setState({
      order: newOrder
    })
    this.props.tabsMove(newOrder)
  }
  renderTabBar = (props, DefaultTabBar) => (
    <DefaultTabBar {...props}>
      {node => (
        <WrapTabNode key={node.key} index={node.key} moveTabNode={this.moveTabNode}>
          {node}
        </WrapTabNode>
      )}
    </DefaultTabBar>
  )
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState)) ||
      !is(fromJS(nextProps.children), fromJS(this.props.children)) ||
      nextProps.tabPosition !== this.props.tabPosition ||
      nextProps.type !== this.props.type
  }
  render() {
    const { order } = this.state
    const { children } = this.props
    const tabs = []
    React.Children.forEach(children, c => {
      tabs.push(c)
    })
    const orderTabs = tabs.slice().sort((a, b) => {
      const orderA = order.indexOf(a.key)
      const orderB = order.indexOf(b.key)
      if (orderA !== -1 && orderB !== -1) {
        return orderA - orderB
      }
      if (orderA !== -1) {
        return -1
      }
      if (orderB !== -1) {
        return 1
      }
      const ia = tabs.indexOf(a)
      const ib = tabs.indexOf(b)
      return ia - ib
    })
    return (
      <DndProvider>
        <Tabs renderTabBar={this.renderTabBar} {...this.props}>
          {orderTabs}
        </Tabs>
      </DndProvider>
    )
  }
}
export default DraggableTabs
src/mob/components/tabs/antv-tabs/index.jsx
New file
@@ -0,0 +1,408 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Tabs, Icon, Popover, Modal } from 'antd'
import MKEmitter from '@/utils/events.js'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import DraggableTabs from './dragabletabs'
import { resetStyle } from '@/utils/utils-custom.js'
import MenuUtils from '@/utils/utils-custom.js'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import './index.scss'
const SettingComponent = asyncIconComponent(() => import('@/menu/components/tabs/tabsetting'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const PasteController = asyncIconComponent(() => import('@/menu/pastecontroller'))
const TabLabelComponent = asyncComponent(() => import('@/menu/components/tabs/tablabelform'))
const TabComponents = asyncComponent(() => import('../tabcomponents'))
const { TabPane } = Tabs
const { confirm } = Modal
class antvTabs extends Component {
  static propTpyes = {
    tabs: PropTypes.object,
    deletecomponent: PropTypes.func,
    updateConfig: PropTypes.func,
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    tabs: null,
    editab: null,
    labelvisible: false
  }
  UNSAFE_componentWillMount () {
    const { tabs } = this.props
    if (tabs.isNew) {
      let _tabs = {
        uuid: tabs.uuid,
        type: tabs.type,
        floor: tabs.floor,
        tabId: tabs.tabId || '',
        parentId: tabs.parentId || '',
        subtype: tabs.subtype,
        width: 24,
        name: tabs.name,
        setting: {width: 24, position: 'top', tabStyle: 'line', name: tabs.name},
        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' },
        subtabs: [
          { uuid: Utils.getuuid(), parentId: tabs.uuid, floor: tabs.floor, label: 'Tab 1', icon: '', components: [] },
          { uuid: Utils.getuuid(), parentId: tabs.uuid, floor: tabs.floor, label: 'Tab 2', icon: '', components: [] },
          { uuid: Utils.getuuid(), parentId: tabs.uuid, floor: tabs.floor, label: 'Tab 3', icon: '', components: [] }
        ]
      }
      this.setState({
        tabs: _tabs
      })
      this.props.updateConfig(_tabs)
    } else {
      this.setState({
        tabs: fromJS(tabs).toJS()
      })
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
    MKEmitter.addListener('submitSearch', this.getSearch)
    MKEmitter.addListener('tabsChange', this.handleTabsChange)
    MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
    MKEmitter.removeListener('submitSearch', this.getSearch)
    MKEmitter.removeListener('tabsChange', this.handleTabsChange)
    MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
  }
  updateComponentStyle = (parentId, keys, style) => {
    const { tabs } = this.state
    if (tabs.subtabs.findIndex(tab => tab.uuid === parentId) === -1) return
    let _tabs = fromJS(tabs).toJS()
    let _tabs_ = fromJS(tabs).toJS()
    let components = []
    _tabs.subtabs.forEach(tab => {
      if (tab.uuid === parentId) {
        components = tab.components.map(item => {
          if (keys.includes(item.uuid)) {
            item.style = {...item.style, ...style}
          }
          return item
        })
        tab.components = []
      }
    })
    _tabs_.subtabs = _tabs_.subtabs.map(tab => {
      if (tab.uuid === parentId) {
        tab.components = components
      }
      return tab
    })
    this.setState({tabs: _tabs}, () => {
      this.updateComponent(_tabs_)
    })
  }
  changeStyle = () => {
    const { tabs } = this.state
    MKEmitter.emit('changeStyle', [tabs.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], tabs.style)
  }
  getStyle = (comIds, style) => {
    const { tabs } = this.state
    if (comIds.length !== 1 || comIds[0] !== tabs.uuid) return
    let _card = {...tabs, style}
    this.setState({
      tabs: _card
    })
    this.props.updateConfig(_card)
  }
  handleTabsChange = (parentId) => {
    const { tabs } = this.state
    if (parentId === tabs.parentId) {
      MKEmitter.emit('tabsChange', tabs.uuid)
    }
  }
  updateComponent = (component) => {
    const { tabs } = this.state
    if (!is(fromJS(tabs.setting), fromJS(component.setting)) || !is(fromJS(tabs.style), fromJS(component.style))) {
      // 注册事件-标签变化,通知标签内元素
      MKEmitter.emit('tabsChange', tabs.uuid)
    }
    component.width = component.setting.width
    component.name = component.setting.name
    this.setState({
      tabs: component
    })
    this.props.updateConfig(component)
  }
  updateTabComponent = (tab) => {
    let tabs = fromJS(this.state.tabs).toJS()
    tabs.subtabs = tabs.subtabs.map(t => {
      if (t.uuid === tab.uuid) {
        return tab
      } else {
        return t
      }
    })
    this.setState({tabs})
    this.props.updateConfig(tabs)
  }
  tabAdd = (e) => {
    const { tabs } = this.state
    e.stopPropagation()
    this.setState({
      editab: {
        uuid: '',
        parentId: tabs.uuid,
        floor: tabs.floor,
        label: '',
        icon: '',
        components: []
      },
      labelvisible: true
    })
  }
  editTab = (tab) => {
    this.setState({
      editab: tab,
      labelvisible: true
    })
  }
  tabLabelSubmit = () => {
    let tabs = fromJS(this.state.tabs).toJS()
    let editab = fromJS(this.state.editab).toJS()
    this.tabLabelRef.handleConfirm().then(res => {
      editab.label = res.label
      editab.icon = res.icon
      editab.hasSearch = res.hasSearch || ''
      editab.blacklist = res.blacklist
      if (editab.uuid) {
        tabs.subtabs = tabs.subtabs.map(t => {
          if (t.uuid === editab.uuid) {
            return editab
          } else {
            return t
          }
        })
      } else {
        editab.uuid = Utils.getuuid()
        tabs.subtabs.push(editab)
      }
      this.setState({
        editab: null,
        labelvisible: false,
        tabs
      })
      this.props.updateConfig(tabs)
    })
  }
  delTab = (tab) => {
    let tabs = fromJS(this.state.tabs).toJS()
    const _this = this
    tabs.subtabs = tabs.subtabs.filter(t => t.uuid !== tab.uuid)
    let uuids = MenuUtils.getDelButtonIds({...tab, type: 'group'})
    confirm({
      title: '确定删除标签?',
      content: '',
      onOk() {
        _this.setState({tabs})
        _this.props.updateConfig(tabs)
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
  }
  moveSwitch = (order) => {
    let tabs = fromJS(this.state.tabs).toJS()
    let subtab = {}
    tabs.subtabs.forEach(item => {
      subtab[item.uuid] = item
    })
    tabs.subtabs = []
    order.forEach(item => {
      if (subtab[item]) {
        tabs.subtabs.push(subtab[item])
      }
    })
    this.setState({tabs})
    this.props.updateConfig(tabs)
  }
  insert = (item, tab) => {
    let tabs = fromJS(this.state.tabs).toJS()
    tabs.subtabs.forEach(stab => {
      if (stab.uuid === tab.uuid) {
        stab.components.push(item)
      }
    })
    this.setState({tabs})
    this.props.updateConfig(tabs)
  }
  getSearch = (config) => {
    const { tabs } = this.state
    if (tabs.uuid !== config.uuid) return
    let _tabs = fromJS(tabs).toJS()
    _tabs.subtabs = _tabs.subtabs.map(t => {
      if (t.uuid === config.tabId) {
        t.search = config.search
      }
      return t
    })
    this.setState({
      tabs: _tabs
    })
    this.props.updateConfig(_tabs)
  }
  setSearch = (tab) => {
    const { tabs } = this.state
    let card = {
      uuid: tabs.uuid,
      tabId: tab.uuid,
      search: tab.search
    }
    if (!card.search) {
      card.search = {
        floor: 1,
        setting: { type: 'title', field: '', title: '', focus: 'true', btn: 'hidden' },
        groups: [],
        fields: []
      }
    }
    MKEmitter.emit('changeSearch', card)
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
      MKEmitter.emit('clickComponent', this.state.tabs)
    }
  }
  render() {
    const { tabs, dict, labelvisible, editab, appType } = this.state
    let _style = resetStyle(tabs.style)
    return (
      <div className={'menu-tabs-edit-box ' + tabs.setting.display} style={_style} onClick={this.clickComponent} id={tabs.uuid}>
        <DraggableTabs tabPosition={tabs.setting.position} type={tabs.setting.tabStyle} tabsMove={this.moveSwitch}>
          {tabs.subtabs.map(tab => (
            <TabPane tab={
              <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                <div className="mk-popover-control">
                  <Icon className="edit" title="edit" type="edit" onClick={() => this.editTab(tab)} />
                  <PasteController type="tab" Tab={tab} insert={this.insert} />
                  <Icon className="close" title="delete" type="close" onClick={() => this.delTab(tab)} />
                </div>
              } trigger="hover">
                <span>{tab.icon ? <Icon type={tab.icon} /> : null}{tab.label}</span>
              </Popover>
            } key={tab.uuid}>
              {appType === 'mob' && tabs.setting.position === 'top' && tabs.setting.display === 'inline-block' && tab.hasSearch === 'icon' ?
                <Icon className="search-icon" onDoubleClick={() => this.setSearch(tab)} type="search" /> : null}
              <TabComponents config={tab} handleList={this.updateTabComponent} deleteCard={this.deleteCard} />
            </TabPane>
          ))}
        </DraggableTabs>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加标签" type="plus" onClick={this.tabAdd} />
            <SettingComponent config={tabs} updateConfig={this.updateComponent} />
            <CopyComponent type="tabs" card={tabs}/>
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(tabs.uuid)} />
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        <Modal
          wrapClassName="popview-modal"
          title={'标签编辑'}
          visible={labelvisible}
          width={600}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.tabLabelSubmit}
          onCancel={() => { this.setState({ labelvisible: false }) }}
          destroyOnClose
        >
          <TabLabelComponent
            dict={dict}
            tab={editab}
            setting={tabs.setting}
            inputSubmit={this.tabLabelSubmit}
            wrappedComponentRef={(inst) => this.tabLabelRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default antvTabs
src/mob/components/tabs/antv-tabs/index.scss
New file
@@ -0,0 +1,122 @@
.menu-tabs-edit-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  >.anticon-tool {
    position: absolute;
    z-index: 2;
    font-size: 16px;
    right: 1px;
    top: 1px;
    cursor: pointer;
    padding: 5px;
    background: rgba(255, 255, 255, 0.55);
  }
  .ant-tabs.ant-tabs-left, .ant-tabs.ant-tabs-bottom {
    .tab-shell-inner {
      padding-top: 25px;
    }
  }
  .ant-tabs-tabpane-active {
    min-height: 200px;
  }
  .ant-tabs .ant-tabs-top-bar > .ant-tabs-nav-container {
    >.ant-tabs-tab-next:not(.ant-tabs-tab-arrow-show) + .ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav {
      width: 100%;
      > div > .ant-tabs-tab-disabled {
        float: right;
      }
    }
  }
  .ant-tabs .ant-tabs-left-bar .ant-tabs-tab {
    text-align: right;
    > span {
      display: inline-block;
      padding: 8px 24px;
    }
  }
  .ant-tabs .ant-tabs-right-bar .ant-tabs-tab {
    text-align: left;
    > span {
      display: inline-block;
      padding: 8px 24px;
    }
  }
  .ant-tabs-tab {
    padding: 0px!important;
    text-align: center;
    > span {
      display: inline-block;
      padding: 12px 16px;
    }
  }
  .ant-tabs-bottom .ant-tabs-bottom-bar .ant-tabs-ink-bar {
    top: 0px;
  }
  .ant-tabs-card {
    .ant-tabs-left-bar, .ant-tabs-right-bar {
      .ant-tabs-tab {
        > span {
          padding: 0px 16px;
        }
      }
      .ant-tabs-tab-active {
        padding-left: 0px!important;
        padding-right: 0px!important;
      }
    }
    .ant-tabs-card-bar {
      .ant-tabs-tab {
        > span {
          display: inline-block;
          padding: 0px 16px;
        }
      }
    }
  }
  .search-icon {
    position: absolute;
    top: 10px;
    right: 40px;
    font-size: 18px;
    cursor: pointer;
    padding: 3px;
  }
}
.menu-tabs-edit-box:hover {
  z-index: 1;
  box-shadow: 0px 0px 4px #1890ff;
}
.mob-shell {
  .menu-tabs-edit-box.flex {
    >.ant-tabs.ant-tabs-top, >.ant-tabs.ant-tabs-bottom {
      >.ant-tabs-bar {
        >.ant-tabs-nav-container {
          >.ant-tabs-nav-wrap {
            >.ant-tabs-nav-scroll {
              >.ant-tabs-nav {
                display: block;
                >div {
                  display: flex;
                  >.ant-tabs-tab {
                    flex: 1;
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
src/mob/components/tabs/tabcomponents/card.jsx
New file
@@ -0,0 +1,105 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const AntvBar = asyncComponent(() => import('@/menu/components/chart/antv-bar'))
const MainSearch = asyncComponent(() => import('@/menu/components/search/main-search'))
const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
const AntvTabs = asyncComponent(() => import('@/mob/components/tabs/antv-tabs'))
const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
const NormalTree = asyncComponent(() => import('@/menu/components/tree/antd-tree'))
const CarouselDataCard = asyncComponent(() => import('@/menu/components/carousel/data-card'))
const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
const NormalForm = asyncComponent(() => import('@/menu/components/form/normal-form'))
const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
const NormalMenuBar = asyncComponent(() => import('@/mob/components/menubar/normal-menubar'))
const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'menu', id, originalIndex, floor: card.floor },
    collect: monitor => ({
      isDragging: monitor.isDragging(),
    }),
  })
  const [, drop] = useDrop({
    accept: 'menu',
    canDrop: () => true,
    drop: (item) => {
      const { id: draggedId, originalIndex, floor } = item
      if (originalIndex === undefined) {
        item.dropTargetId = id
      } else if (draggedId && floor === card.floor) {
        if (draggedId === id) return
        const { index: originIndex } = findCard(draggedId)
        if (originIndex === -1) return
        const { index: overIndex } = findCard(id)
        moveCard(draggedId, overIndex)
      }
    }
  })
  let style = { opacity: 1}
  if (isDragging) {
    style = { opacity: 0.3}
  }
  const getCardComponent = () => {
    if (card.type === 'bar' || card.type === 'line') {
      return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
    } else if (card.type === 'search') {
      return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'pie') {
      return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'dashboard') {
      return (<AntvDashboard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'tree') {
      return (<NormalTree card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'scatter') {
      return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'form') {
      return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'tabs') {
      return (<AntvTabs tabs={card} updateConfig={updateConfig} deletecomponent={delCard} />)
    } else if (card.type === 'card' && card.subtype === 'datacard') {
      return (<DataCard card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
    } else if (card.type === 'card' && card.subtype === 'propcard') {
      return (<PropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'carousel' && card.subtype === 'datacard') {
      return (<CarouselDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'carousel' && card.subtype === 'propcard') {
      return (<CarouselPropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'table' && card.subtype === 'tablecard') {
      return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'table' && card.subtype === 'normaltable') {
      return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'group' && card.subtype === 'normalgroup') {
      return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'editor') {
      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'menubar') {
      return (<NormalMenuBar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'code') {
      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    }
  }
  return (
    <div className={'ant-col mk-component-card ant-col-' + (card.width || 24)} ref={node => drag(drop(node))} style={style}>
      {getCardComponent()}
    </div>
  )
}
export default Card
src/mob/components/tabs/tabcomponents/index.jsx
New file
@@ -0,0 +1,172 @@
import React, { useState } from 'react'
import { useDrop } from 'react-dnd'
import { is, fromJS } from 'immutable'
import update from 'immutability-helper'
import { Empty, notification, Modal } from 'antd'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import MenuUtils from '@/utils/utils-custom.js'
import Card from './card'
import './index.scss'
const { confirm } = Modal
const Container = ({ config, handleList }) => {
  const [cards, setCards] = useState(config.components)
  const moveCard = (id, atIndex) => {
    const { card, index } = findCard(id)
    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
    handleList({...config, components: _cards})
  }
  if (!is(fromJS(cards), fromJS(config.components))) {
    setCards(config.components)
  }
  const findCard = id => {
    const card = cards.filter(c => `${c.uuid}` === id)[0]
    return {
      card,
      index: cards.indexOf(card),
    }
  }
  const updateConfig = (element) => {
    handleList({...config, components: cards.map(item => item.uuid === element.uuid ? element : item)})
  }
  const deleteCard = (id) => {
    const { card } = findCard(id)
    let hasComponent = false
    if (card.type === 'tabs') {
      card.subtabs.forEach(tab => {
        if (tab.components.length > 0) {
          hasComponent = true
        }
      })
    }
    let uuids = MenuUtils.getDelButtonIds(card)
    confirm({
      title: `确定删除《${card.name}》吗?`,
      content: hasComponent ? '当前组件中含有子组件!' : '',
      onOk() {
        handleList({...config, components: cards.filter(item => item.uuid !== card.uuid)})
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
  }
  const [, drop] = useDrop({
    accept: 'menu',
    drop(item) {
      if (item.hasOwnProperty('originalIndex') || item.added) {
        return
      }
      item.added = true
      if (item.component === 'search') { // 搜索组件不可重复添加
        if (cards.filter(card => card.type === 'search').length > 0) {
          notification.warning({
            top: 92,
            message: '搜索条件不可重复添加!',
            duration: 5
          })
          return
        }
      } else if (item.component === 'tabs' && config.floor === 3) {
        notification.warning({
          top: 92,
          message: '标签页最多为三重结构!',
          duration: 5
        })
        return
      }
      let name = ''
      let names = {
        bbar: '柱状图',
        line: '折线图',
        tabs: '标签组',
        pie: '饼图',
        search: '搜索',
        table: '表格',
        group: '分组',
        editor: '富文本',
        code: '自定义',
        carousel: '轮播',
        form: '表单',
        dashboard: '仪表盘',
        scatter: '散点图',
        menubar: '菜单栏',
        tree: '树形列表',
        card: '卡片'
      }
      let i = 1
      while (!name && names[item.component]) {
        let _name = names[item.component] + i
        if (config.components.filter(com => com.name === _name).length === 0) {
          name = _name
        }
        i++
      }
      let newcard = {
        uuid: Utils.getuuid(),
        tabId: config.uuid,
        parentId: config.parentId,
        type: item.component,
        subtype: item.subtype,
        config: item.config,
        width: item.width || 24,
        dataName: Utils.getdataName(),
        name: name,
        floor: config.floor ? (config.floor + 1) : 2, // 组件的层级
        isNew: true                                   // 新添加标志,用于初始化
      }
      let targetId = ''
      if (item.dropTargetId) {
        targetId = item.dropTargetId
        delete item.dropTargetId
      } else if (cards.length > 0) {
        targetId = cards.slice(-1)[0].uuid
      }
      const { index: overIndex } = findCard(`${targetId}`)
      const _cards = update(cards, { $splice: [[overIndex + 1, 0, newcard]] })
      handleList({...config, components: _cards})
    }
  })
  return (
    <div ref={drop} className="ant-row tab-shell-inner">
      {cards.map(card => (
        <Card
          id={card.uuid}
          key={card.uuid}
          config={config}
          card={card}
          moveCard={moveCard}
          delCard={deleteCard}
          findCard={findCard}
          updateConfig={updateConfig}
        />
      ))}
      {cards.length === 0 ?
        <Empty description="请添加组件" /> : null
      }
    </div>
  )
}
export default Container
src/mob/components/tabs/tabcomponents/index.scss
New file
@@ -0,0 +1,14 @@
.tab-shell-inner {
  margin: 0px;
  .anticon {
    cursor: unset;
  }
  .mk-component-card {
    position: relative;
  }
  >.ant-empty {
    padding: 60px 0px 70px;
  }
}
src/mob/components/topbar/normal-navbar/index.jsx
@@ -158,13 +158,8 @@
  render() {
    const { card } = this.state
    let _style = {...card.style}
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    return (
      <div className="normal-topbar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
      <div className="normal-topbar-edit-box" style={card.style} onClick={this.clickComponent} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <WrapComponent config={card} updateConfig={this.updateComponent} />
src/mob/mobshell/card.jsx
@@ -9,7 +9,7 @@
const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
const AntvTabs = asyncComponent(() => import('@/mob/components/tabs/antv-tabs'))
const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
const CarouselDataCard = asyncComponent(() => import('@/menu/components/carousel/data-card'))
src/mob/mobshell/index.jsx
@@ -122,6 +122,7 @@
        card: '卡片',
        navbar: '导航栏',
        menubar: '菜单栏',
        balcony: '浮动卡',
        login: '登录'
      }
      let i = 1
src/pc/components/navbar/normal-navbar/index.jsx
@@ -166,13 +166,8 @@
  render() {
    const { card } = this.state
    let _style = {...card.style}
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    return (
      <div className="normal-navbar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
      <div className="normal-navbar-edit-box" style={card.style} onClick={this.clickComponent} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <MenuComponent config={card} updateConfig={this.updateComponent} />
src/pc/menushell/index.jsx
@@ -112,6 +112,7 @@
        dashboard: '仪表盘',
        tree: '树形列表',
        card: '卡片',
        balcony: '浮动卡',
        login: '登录'
      }
      let i = 1
src/tabviews/rolemanage/index.jsx
@@ -1,7 +1,8 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Card, Col, Row, Icon, Menu, notification, Spin, Input, Tabs, Button, Tree, Empty } from 'antd'
import { Card, Col, Row, Icon, Menu, notification, Spin, Input, Tabs, Button, Tree, Empty, Select } from 'antd'
import md5 from 'md5'
import Api from '@/api'
import Utils from '@/utils/utils.js'
@@ -20,14 +21,16 @@
export default class RoleManage extends Component {
  static propTpyes = {
    MenuNo: PropTypes.string, // 菜单参数
    MenuID: PropTypes.string // 菜单Id
    MenuID: PropTypes.string  // 菜单Id
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    loading: true,
    loadingTree: false,
    loadingAppTree: false,
    roleList: null,
    filterRoleList: [],
    selectRoleId: '',
    mainMenus: null,
    menuTrees: null,
@@ -36,9 +39,17 @@
    selectMenuTrees: null,
    selectMenuOpenKeys: [],
    primarykey: '',
    tabKey: '',
    submitloading: false,
    initCheckKeys: null
    initCheckKeys: null,
    activeKey: 'manage',
    appTrees: null,
    appCheckedKeys: [],
    appOpenKeys: [],
    applist: [],
    selectApp: null,
    selectSubApp: null,
    selectAppTrees: null,
    selectAppOpenKeys: [],
  }
  /**
@@ -52,7 +63,8 @@
    if (result.status) {
      this.setState({
        roleList: result.data
        roleList: result.data,
        filterRoleList: result.data || []
      })
    } else {
      notification.warning({
@@ -61,6 +73,35 @@
        duration: 5
      })
    }
  }
  getAppList = () => {
    let param = {
      func: 's_get_kei'
    }
    Api.getCloudConfig(param).then(result => {
      if (result.status) {
        let applist = result.data.map(item => {
          item.sublist = item.data_detail || []
          item.sublist = item.sublist.map(cell => {
            cell.ID = cell.d_id
            return cell
          })
          return item
        })
        let selectApp = applist[0] || null
        this.setState({ applist, selectApp })
      } else {
        notification.warning({
          top: 92,
          message: result.message,
          duration: 5
        })
      }
    })
  }
  /**
@@ -103,12 +144,12 @@
   * @description 获取所有菜单节点,形成权限树
   */
  getAllMenuList = async () => {
    const { selectRoleId, mainMenus, tabKey } = this.state
    const { selectRoleId, mainMenus } = this.state
    let param = {
      func: 's_rolemenu_get_FunMenu',
      RoleID: selectRoleId,
      SelectedType: tabKey
      SelectedType: ''
    }
    let result = await Api.getSystemConfig(param)
@@ -153,61 +194,147 @@
   * @description 选择角色且存在权限树时,获取已分配结构
   */
  getSelectMenuList = async () => {
    const { selectRoleId, menuTrees, tabKey } = this.state
    if (!menuTrees) return
    const { selectRoleId, menuTrees, activeKey } = this.state
    if (!menuTrees || !selectRoleId || activeKey !== 'manage') return
    let param = {
      func: 's_rolemenu_get_Menulist',
      RoleID: selectRoleId
    }
    this.setState({
      loadingTree: true
    })
    let result = await Api.getSystemConfig(param)
    if (result.status) {
      let _openKeys = []
      if (tabKey === '') {
        let _initKeys = result.data.map(item => item.MenuID)
        _initKeys = Array.from(new Set(_initKeys))
      let _initKeys = result.data.map(item => item.MenuID)
      _initKeys = Array.from(new Set(_initKeys))
        selectMap = new Map()
        this.getCheckedKeys(fromJS(menuTrees).toJS(), _initKeys)
      selectMap = new Map()
      this.getCheckedKeys(fromJS(menuTrees).toJS(), _initKeys)
        if (menuTrees[0]) {
          if (menuTrees[0].key === 'PC' && menuTrees[0].children) {
            this.getOpenNode(menuTrees[0].children[0], _openKeys)
          } else {
            this.getOpenNode(menuTrees[0], _openKeys)
          }
      if (menuTrees[0]) {
        if (menuTrees[0].key === 'PC' && menuTrees[0].children) {
          this.getOpenNode(menuTrees[0].children[0], _openKeys)
        } else {
          this.getOpenNode(menuTrees[0], _openKeys)
        }
        this.setState({
          loadingTree: false,
          initCheckKeys: _initKeys,
          checkedKeys: Array.from(selectMap.keys()),
          menuOpenKeys: _openKeys
        })
      } else {
        let Keys = result.data.map(item => item.MenuID)
        let _tree = this.getSelectTree(fromJS(menuTrees).toJS(), Keys)
        if (_tree[0]) {
          if (_tree[0].key === 'PC' && _tree[0].children) {
            this.getOpenNode(_tree[0].children[0], _openKeys)
          } else {
            this.getOpenNode(_tree[0], _openKeys)
          }
        }
        this.setState({
          loadingTree: false,
          selectMenuTrees: _tree,
          selectMenuOpenKeys: _openKeys
        })
      }
      let _openkeys = []
      let Keys = result.data.map(item => item.MenuID)
      let _tree = this.getSelectTree(fromJS(menuTrees).toJS(), Keys)
      if (_tree[0]) {
        if (_tree[0].key === 'PC' && _tree[0].children) {
          this.getOpenNode(_tree[0].children[0], _openkeys)
        } else {
          this.getOpenNode(_tree[0], _openkeys)
        }
      }
      this.setState({
        loadingTree: false,
        initCheckKeys: _initKeys,
        checkedKeys: Array.from(selectMap.keys()),
        menuOpenKeys: _openKeys,
        selectMenuTrees: _tree,
        selectMenuOpenKeys: _openkeys
      })
    } else {
      this.setState({
        loadingTree: false
      })
      notification.warning({
        top: 92,
        message: result.message,
        duration: 5
      })
    }
  }
  /**
   * @description 选择角色且存在权限树时,获取已分配结构
   */
  getSelectAppNodeList = async () => {
    const { selectRoleId, selectSubApp, selectApp, appTrees, activeKey } = this.state
    if (!appTrees || !selectRoleId || activeKey !== 'app' || !selectSubApp || !selectApp) return
    let param = {
      func: 's_rolemenu_get_Menulist',
      RoleID: selectRoleId,
      TypeCharOne: selectApp.kei_no,
      typename: selectSubApp.typename,
      lang: selectSubApp.lang
    }
    this.setState({
      loadingAppTree: true
    })
    let result = await Api.getSystemConfig(param)
    if (result.status) {
      let _initKeys = result.data.map(item => item.MenuID)
      _initKeys = Array.from(new Set(_initKeys))
      let _checkedKeys = []
      let getCheckedKeys = (parents) => {
        parents.forEach(item => {
          if (_initKeys.includes(item.key)) {
            if (item.children && item.children.length > 0) {
              getCheckedKeys(item.children)
            } else {
              _checkedKeys.push(item.key)
            }
          }
        })
      }
      getCheckedKeys(appTrees)
      let _openKeys = []
      let getOpenNode = (parentNode) => {
        if (parentNode && parentNode.children && parentNode.children.length > 0) {
          _openKeys.push(parentNode.key)
          parentNode.children.forEach(node => {
            getOpenNode(node)
          })
        }
      }
      getOpenNode(appTrees[0])
      let Keys = result.data.map(item => item.MenuID)
      let _tree = this.getSelectTree(fromJS(appTrees).toJS(), Keys)
      let _openkeys = []
      let _getOpenNode = (parentNode) => {
        if (parentNode && parentNode.children && parentNode.children.length > 0) {
          _openkeys.push(parentNode.key)
          parentNode.children.forEach(node => {
            _getOpenNode(node)
          })
        }
      }
      _getOpenNode(_tree[0])
      this.setState({
        loadingAppTree: false,
        appInitCheckKeys: _initKeys,
        appCheckedKeys: _checkedKeys,
        appOpenKeys: _openKeys,
        selectAppTrees: _tree,
        selectAppOpenKeys: _openkeys
      })
    } else {
      this.setState({
        loadingAppTree: false
      })
      notification.warning({
        top: 92,
@@ -334,10 +461,10 @@
    if (selectRoleId === role.RoleID) return
    this.setState({
      selectRoleId: role.RoleID,
      loadingTree: true
      selectRoleId: role.RoleID
    }, () => {
      this.getSelectMenuList()
      this.getSelectAppNodeList()
    })
  }
@@ -369,23 +496,13 @@
  }
  /**
   * @description 已分配与未分配切换
   * @description 节点选择事件
   */
  changeTab = (key) => {
    const { selectRoleId } = this.state
  onAppCheck = (checkedKeys, info) => {
    this.setState({
      tabKey: key === 'selected' ? key : '',
      loadingTree: true
    }, () => {
      if (selectRoleId) {
        this.getSelectMenuList()
      } else {
        this.setState({
          selectMenuTrees: [],
          loadingTree: false
        })
      }
      appCheckedKeys: checkedKeys,
      appHalfCheckedKeys: info.halfCheckedKeys,
      appInitCheckKeys: null
    })
  }
@@ -429,36 +546,88 @@
    Api.getSystemConfig(param).then(result => {
      if (result.status) {
        if (!window.GLOB.mainSystemApi) {
          notification.success({
            top: 92,
            message: '保存成功',
            duration: 2
          })
          this.setState({
            submitloading: false,
            loadingTree: true
          }, () => {
            this.getSelectMenuList()
          })
        } else {
        notification.success({
          top: 92,
          message: '保存成功',
          duration: 2
        })
        this.setState({
          submitloading: false
        }, () => {
          this.getSelectMenuList()
        })
        if (window.GLOB.mainSystemApi) {
          Api.getLocalConfig(localParam).then(res => {
            if (res.status) {
              notification.success({
            if (!res.status) {
              notification.warning({
                top: 92,
                message: '保存成功',
                duration: 2
                message: res.message,
                duration: 5
              })
              this.setState({
                submitloading: false,
                loadingTree: true
              }, () => {
                this.getSelectMenuList()
              })
            } else {
              this.setState({
                submitloading: false
              })
            }
          })
        }
      } else {
        this.setState({
          submitloading: false
        })
        notification.warning({
          top: 92,
          message: result.message,
          duration: 5
        })
      }
    })
  }
  /**
   * @description 提交已选的权限
   */
  appRoleSubmit = () => {
    const { selectApp, selectSubApp, appCheckedKeys, appHalfCheckedKeys, selectRoleId, appInitCheckKeys } = this.state
    let _keys = []
    if (appInitCheckKeys) {
      _keys = appInitCheckKeys
    } else {
      _keys = appCheckedKeys.concat(appHalfCheckedKeys)
    }
    let param = {
      func: 's_rolemenu_sub',
      RoleID: selectRoleId,
      TypeCharOne: selectApp.kei_no,
      typename: selectSubApp.typename,
      lang: selectSubApp.lang,
      RoleMenu: _keys.map(key => {
        return {MenuID: key}
      })
    }
    let localParam = fromJS(param).toJS()
    localParam.func = 's_rolemenu_sub_local'
    this.setState({
      submitloading: true
    })
    Api.getSystemConfig(param).then(result => {
      if (result.status) {
        notification.success({
          top: 92,
          message: '保存成功',
          duration: 2
        })
        this.setState({
          submitloading: false
        }, () => {
          this.getSelectAppNodeList()
        })
        if (window.GLOB.mainSystemApi) {
          Api.getLocalConfig(localParam).then(res => {
            if (!res.status) {
              notification.warning({
                top: 92,
                message: res.message,
@@ -489,25 +658,168 @@
      loading: true,
      loadingTree: false,
      roleList: null,
      filterRoleList: [],
      selectRoleId: '',
      mainMenus: null,
      menuTrees: null,
      checkedKeys: [],
      appCheckedKeys: [],
      menuOpenKeys: [],
      selectMenuTrees: null,
      selectMenuOpenKeys: [],
      primarykey: '',
      tabKey: '',
      submitloading: false,
      initCheckKeys: null
      initCheckKeys: null,
      appInitCheckKeys: null,
      activeKey: 'manage',
      appTrees: null,
      appOpenKeys: [],
      applist: [],
      selectApp: null,
      selectSubApp: null,
      selectAppTrees: null,
      selectAppOpenKeys: []
    })
    this.getRoleList()
    this.getMainMenuList()
    this.getAppList()
  }
  filterRole = () => {
    const { primarykey, roleList } = this.state
    let _roleList  = []
    if (roleList && roleList.length > 0) {
      _roleList = roleList.filter(role => role.RoleName.toLowerCase().indexOf(primarykey.toLowerCase()) >= 0)
    }
    this.setState({filterRoleList: _roleList})
  }
  getTreeList = () => {
    const { selectApp, selectSubApp } = this.state
    let param = {
      func: 's_get_menus_roles_tree',
      typecharone: selectApp.kei_no,
      lang: selectSubApp.lang
    }
    param.upid = md5(window.GLOB.appkey + selectApp.kei_no + selectSubApp.typename + selectSubApp.lang)
    this.setState({loadingAppTree: true})
    Api.getCloudConfig(param).then(result => {
      if (result.status) {
        if (!result.data || result.data.length === 0) {
          this.setState({loadingAppTree: false, appTrees: []})
        } else {
          this.initTrees(result.data)
        }
      } else {
        this.setState({loadingAppTree: false, appTrees: []})
        notification.warning({
          top: 92,
          message: result.message,
          duration: 5
        })
      }
    })
  }
  initTrees = (data) => {
    let trees = []
    let map = new Map()
    let _data = data.sort((a, b) => {
      return a.sort - b.sort
    })
    _data.forEach(menu => {
      if (menu.ParentID === 'top') {
        trees.push({
          key: menu.MenuID,
          title: menu.MenuName,
          children: []
        })
      } else {
        map.set(menu.MenuID, menu)
      }
    })
    let reset = (m) => {
      return m.map(n => {
        [...map.keys()].forEach(key => {
          if (map.get(key).ParentID === n.key) {
            let c = map.get(key)
            n.children.push({
              key: c.MenuID,
              title: c.MenuName,
              children: []
            })
            map.delete(key)
          }
        })
        if (n.children.length > 0) {
          n.children = reset(n.children)
        }
        return n
      })
    }
    trees = reset(trees)
    let expandedKeys = this.getExpandedKeys(trees, 0, [])
    this.setState({loadingAppTree: false, appTrees: trees, appOpenKeys: expandedKeys}, () => {
      this.getSelectAppNodeList()
    })
  }
  getExpandedKeys = (trees, i, keys) => {
    if (i >= 3 || !trees[0]) return keys
    keys.push(trees[0].key)
    i++
    if (trees[0].children && trees[0].children.length > 0) {
      keys = this.getExpandedKeys(trees[0].children, i, keys)
    }
    return keys
  }
  changeType = (val) => {
    this.setState({
      activeKey: val
    }, () => {
      this.getSelectMenuList()
      this.getSelectAppNodeList()
    })
  }
  changeApp = (val) => {
    const { applist } = this.state
    let app = applist.filter(item => item.ID === val)[0]
    this.setState({selectApp: app, selectSubApp: null})
  }
  changeSubApp = (val) => {
    const { selectApp } = this.state
    let subapp = selectApp.sublist.filter(item => item.ID === val)[0]
    this.setState({selectSubApp: subapp}, () => {
      this.getTreeList()
    })
  }
  UNSAFE_componentWillMount () {
    this.getRoleList()
    this.getMainMenuList()
    this.getAppList()
  }
  componentDidMount () {
@@ -529,66 +841,134 @@
  }
  render() {
    const { roleList, loading, loadingTree, primarykey, menuTrees, checkedKeys, menuOpenKeys, selectMenuTrees, tabKey, selectRoleId, selectMenuOpenKeys, submitloading } = this.state
    let _roleList  = []
    if (roleList && roleList.length > 0) {
      _roleList = roleList.filter(role => role.RoleName.toLowerCase().indexOf(primarykey.toLowerCase()) >= 0)
    }
    const { activeKey, filterRoleList, applist, selectApp, selectSubApp, loading, loadingTree, loadingAppTree, primarykey, menuTrees, appTrees, checkedKeys, appCheckedKeys, menuOpenKeys, selectMenuTrees, selectAppTrees, selectRoleId, selectMenuOpenKeys, selectAppOpenKeys, submitloading, appOpenKeys } = this.state
    return (
      <div className="rolemanage">
        {loading && <Spin size="large" />}
        <Row gutter={16}>
          <Col span={5}>
            <Card
              className="role-list"
              title={
                <span className="role-title">
                  <Icon type="bank" />
                  <span className="title">{this.state.dict['main.role.title']}</span>
                  <Search placeholder="" onSearch={value => this.setState({primarykey: value})} />
                </span>
              }
              bordered={false}
            >
              <Menu
                onClick={this.handleClick}
                mode="inline"
              >
                {_roleList.map((role, index) =>
                  <Menu.Item key={index} onClick={() => this.changeRole(role)}>{role.RoleName}</Menu.Item>
                )}
              </Menu>
            </Card>
          </Col>
          <Col span={19}>
            <Tabs defaultActiveKey="all" tabBarExtraContent={!tabKey && selectRoleId ? <Button type="primary" loading={submitloading} onClick={this.roleSubmit}>提交</Button> : null} onChange={this.changeTab}>
              <TabPane tab="菜单列表" key="all">
                {!loadingTree && menuTrees && menuTrees.length > 0 ? <Tree
                  checkable
                  selectable={false}
                  defaultExpandedKeys={menuOpenKeys}
                  autoExpandParent={true}
                  onCheck={this.onCheck}
                  checkedKeys={checkedKeys}
        <Tabs activeKey={activeKey} type="card" onChange={this.changeType}>
          <TabPane tab="管理系统" key="manage">
            <Row gutter={16}>
              <Col span={5}>
                <Card
                  className="role-list"
                  title={
                    <span className="role-title">
                      <Icon type="bank" />
                      <span className="title">角色</span>
                      <Search placeholder="" value={primarykey} onChange={e => this.setState({primarykey: e.target.value})} onSearch={this.filterRole} />
                    </span>
                  }
                  bordered={false}
                >
                  {this.renderTreeNodes(menuTrees)}
                </Tree> : null}
                {!loadingTree && (!menuTrees || menuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
                {loadingTree ? <Spin className="load-tree" /> : null}
              </TabPane>
              <TabPane tab="已授权菜单" key="selected">
                {!loadingTree && selectMenuTrees && selectMenuTrees.length > 0 ? <DirectoryTree multiple defaultExpandedKeys={selectMenuOpenKeys}>
                  {this.renderTreeNodes(selectMenuTrees)}
                </DirectoryTree> : null}
                {!loadingTree && (!selectMenuTrees || selectMenuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
                {loadingTree ? <Spin className="load-tree" /> : null}
              </TabPane>
            </Tabs>
          </Col>
        </Row>
                  <Menu selectedKeys={[selectRoleId]} mode="inline">
                    {filterRoleList.map((role) =>
                      <Menu.Item key={role.RoleID} onClick={() => this.changeRole(role)}>{role.RoleName}</Menu.Item>
                    )}
                  </Menu>
                </Card>
              </Col>
              <Col span={19}>
                <Tabs defaultActiveKey="all">
                  <TabPane tab="权限" key="all">
                    {selectRoleId ? <Button className="submitBtn" type="primary" loading={submitloading} onClick={this.roleSubmit}>提交</Button> : null}
                    {!loadingTree && menuTrees && menuTrees.length > 0 ? <Tree
                      checkable
                      selectable={false}
                      defaultExpandedKeys={menuOpenKeys}
                      autoExpandParent={true}
                      onCheck={this.onCheck}
                      checkedKeys={checkedKeys}
                    >
                      {this.renderTreeNodes(menuTrees)}
                    </Tree> : null}
                    {!loadingTree && (!menuTrees || menuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
                    {loadingTree ? <Spin className="load-tree" /> : null}
                  </TabPane>
                  <TabPane tab="已授权" key="selected">
                    {!loadingTree && selectMenuTrees && selectMenuTrees.length > 0 ? <DirectoryTree multiple defaultExpandedKeys={selectMenuOpenKeys}>
                      {this.renderTreeNodes(selectMenuTrees)}
                    </DirectoryTree> : null}
                    {!loadingTree && (!selectMenuTrees || selectMenuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
                    {loadingTree ? <Spin className="load-tree" /> : null}
                  </TabPane>
                </Tabs>
              </Col>
            </Row>
          </TabPane>
          <TabPane tab="应用" key="app">
            <div className="app-select">
              <div className="mk-form-item">
                <label>应用:</label>
                <Select value={selectApp ? selectApp.ID : ''} onChange={this.changeApp}>
                  {applist.map(option =>
                    <Select.Option key={option.ID} value={option.ID}>{option.remark}</Select.Option>
                  )}
                </Select>
              </div>
              <div className="mk-form-item">
                <label>子应用:</label>
                <Select value={selectSubApp ? selectSubApp.ID : ''} onChange={this.changeSubApp}>
                  {selectApp && selectApp.sublist.map(option =>
                    <Select.Option key={option.ID} value={option.ID}>{`${option.typename}(${option.lang})`}</Select.Option>
                  )}
                </Select>
              </div>
            </div>
            <Row gutter={16}>
              <Col span={5}>
                <Card
                  className="role-list"
                  title={
                    <span className="role-title">
                      <Icon type="bank" />
                      <span className="title">角色</span>
                      <Search placeholder="" value={primarykey} onChange={e => this.setState({primarykey: e.target.value})} onSearch={this.filterRole} />
                    </span>
                  }
                  bordered={false}
                >
                  <Menu selectedKeys={[selectRoleId]} mode="inline">
                    {filterRoleList.map((role) =>
                      <Menu.Item key={role.RoleID} onClick={() => this.changeRole(role)}>{role.RoleName}</Menu.Item>
                    )}
                  </Menu>
                </Card>
              </Col>
              <Col span={19}>
                <Tabs defaultActiveKey="all">
                  <TabPane tab="权限" key="all">
                    {selectSubApp && selectRoleId ? <Button className="submitBtn" type="primary" loading={submitloading} onClick={this.appRoleSubmit}>提交</Button> : null}
                    {selectSubApp ? <div>
                      {!loadingAppTree && appTrees && appTrees.length > 0 ? <Tree
                        checkable
                        selectable={false}
                        defaultExpandedKeys={appOpenKeys}
                        autoExpandParent={true}
                        onCheck={this.onAppCheck}
                        checkedKeys={appCheckedKeys}
                      >
                        {this.renderTreeNodes(appTrees)}
                      </Tree> : null}
                      {!loadingAppTree && (!appTrees || appTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
                      {loadingAppTree ? <Spin className="load-tree" /> : null}
                    </div> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={'请选择子应用'} />}
                  </TabPane>
                  <TabPane tab="已授权" key="selected">
                    {selectSubApp ? <div>
                      {!loadingAppTree && selectAppTrees && selectAppTrees.length > 0 ? <DirectoryTree multiple defaultExpandedKeys={selectAppOpenKeys}>
                        {this.renderTreeNodes(selectAppTrees)}
                      </DirectoryTree> : null}
                      {!loadingAppTree && (!selectAppTrees || selectAppTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
                      {loadingAppTree ? <Spin className="load-tree" /> : null}
                    </div> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={'请选择子应用'} />}
                  </TabPane>
                </Tabs>
              </Col>
            </Row>
          </TabPane>
        </Tabs>
      </div>
    )
  }
src/tabviews/rolemanage/index.scss
@@ -14,7 +14,7 @@
        .role-title {
          display: inline-block;
          width: 100%;
          color: #1890ff;
          color: rgba(0, 0, 0, 0.65);
          .anticon-bank {
            margin-right: 5px;
          }
@@ -45,8 +45,14 @@
    left: calc(50vw - 22px);
    top: calc(50vh - 70px);
    z-index: 1;
  }
  .ant-tabs {
  }
  .ant-tabs-bar.ant-tabs-card-bar {
    .ant-tabs-tab {
      min-width: 120px;
      text-align: center;
    }
  }
  .ant-tabs.ant-tabs-line {
    background: #fff;
    min-height: calc(100vh - 125px);
    box-shadow: 0px 0px 2px #eeeeee;
@@ -74,6 +80,30 @@
        text-align: center;
        color: #bcbcbc;
      }
      .submitBtn {
        position: absolute;
        right: 10px;
        top: 15px;
      }
    }
  }
  .app-select {
    position: absolute;
    top: 0px;
    right: 0px;
    .mk-form-item {
      display: inline-block;
      width: 230px;
      label {
        width: 60px;
        display: inline-block;
        text-align: right;
        padding-right: 5px;
        white-space: nowrap;
      }
      .ant-select {
        width: 150px;
      }
    }
  }
}