king
2020-10-26 c7df940632b5f238f524da651fbf27a91ff6ad36
2020-10-26
29个文件已修改
7个文件已添加
949 ■■■■■ 已修改文件
src/assets/css/main.scss 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/action.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/card.jsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/elementform/index.jsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/formconfig.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/index.jsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/settingform/index.jsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.scss 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/wrapsetting/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/wrapsetting/settingform/index.jsx 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/prop-card/index.jsx 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/prop-card/index.scss 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/index.scss 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/index.scss 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/antv-tabs/index.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/card.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/card.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/index.scss 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modelsource/option.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/padcontroller/index.jsx 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/padcontroller/index.scss 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/index.jsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardcellList/index.jsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.scss 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/prop-card/asyncButtonComponent.jsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/prop-card/index.jsx 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/prop-card/index.scss 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-bar-line/index.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-pie/index.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/tabs/antv-tabs/index.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/index.jsx 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/events.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.jsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/main.scss
@@ -9,6 +9,9 @@
  -webkit-font-smoothing: antialiased;
  box-sizing: border-box;
  font-weight: normal;
  border-style: solid;
  border-width: 0;
  border-color: transparent;
  &:hover {
    outline: none;
  }
@@ -254,7 +257,7 @@
.popview-modal {
  .ant-modal-body {
    min-height: 300px;
    min-height: 250px;
    max-height: calc(100vh - 190px);
    overflow-y: auto;
  }
src/menu/components/card/cardcellcomponent/dragaction/action.jsx
@@ -62,8 +62,8 @@
  return (
    <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={() => editCard(id)} />
        <Icon className="close" title="close" type="close" onClick={() => delCard(id)} />
        <Icon className="edit" title="编辑" type="edit" onClick={() => editCard(id)} />
        <Icon className="close" title="删除" type="close" onClick={() => delCard(id)} />
        <Icon className="style" title="调整样式" onClick={() => changeStyle(id)} type="font-colors" />
        {hasProfile ? <Icon className="profile" title="setting" type="profile" onClick={() => profileCard(id)} /> : null}
      </div>
src/menu/components/card/cardcellcomponent/dragaction/card.jsx
@@ -44,11 +44,12 @@
    } else if (card.eleType === 'icon') {
      return (<Icon type={card.icon}/>)
    } else if (card.eleType === 'slider') {
      let val = card.value ? (card.value / card.maxValue) * 100 : 30
      return (
        <div className="ant-mk-slider">
          <div className="ant-mk-slider-rail"></div>
          <div className="ant-mk-slider-track" style={{width: '30%', backgroundColor: card.color}}></div>
          <div className="ant-mk-slider-handle" style={{left: '30%', borderColor: card.color}}></div>
          <div className="ant-mk-slider-track" style={{width: `${val}%`, backgroundColor: card.color}}></div>
          <div className="ant-mk-slider-handle" style={{left: `${val}%`, borderColor: card.color}}></div>
        </div>
      )
    } else if (card.eleType === 'picture') {
@@ -91,8 +92,8 @@
  return (
    <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={() => editCard(id)} />
        <Icon className="close" title="close" type="close" onClick={() => delCard(id)} />
        <Icon className="edit" title="编辑" type="edit" onClick={() => editCard(id)} />
        <Icon className="close" title="删除" type="close" onClick={() => delCard(id)} />
        <Icon className="style" title="调整样式" onClick={() => changeStyle(id)} type="font-colors" />
      </div>
    } trigger="hover">
src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -14,7 +14,7 @@
  picture: ['eleType', 'datatype', 'width', 'lenWidRadio', 'radius', 'padding', 'url'],
  icon: ['eleType', 'icon', 'datatype', 'fontSize', 'width', 'align', 'padding'],
  link: ['eleType', 'datatype', 'labelfield', 'fontSize', 'width', 'height', 'align', 'padding', 'prefix'],
  slider: ['eleType', 'field', 'width', 'color', 'padding', 'maxValue'],
  slider: ['eleType', 'datatype', 'width', 'color', 'padding', 'maxValue'],
  splitline: ['eleType', 'color', 'width', 'padding'],
}
@@ -37,7 +37,7 @@
  UNSAFE_componentWillMount () {
    const { card, config } = this.props
    let _options = this.getOptions(card.eleType, card.datatype)
    this.setState({
      eleType: card.eleType,
      datatype: card.datatype,
@@ -69,6 +69,9 @@
              })
            }
          })
        } else if (item.key === 'value' && card.eleType === 'slider') {
          item.type = 'number'
          item.label = '值'
        }
        return item
@@ -79,7 +82,7 @@
  getOptions = (eleType, datatype) => {
    let _options = fromJS(cardTypeOptions[eleType]).toJS() // 选项列表
    
    if (['text', 'number', 'picture', 'link'].includes(eleType)) {
    if (['text', 'number', 'picture', 'link', 'slider'].includes(eleType)) {
      if (datatype === 'dynamic') {
        _options.push('field')
      } else if (eleType !== 'picture') {
@@ -127,6 +130,14 @@
              })
            }
          })
        } else if (item.key === 'value') {
          if (value === 'slider') {
            item.type = 'number'
            item.label = '值'
          } else {
            item.type = 'text'
            item.label = '内容'
          }
        }
        return item
src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -99,6 +99,7 @@
    {
      type: 'text',
      key: 'value',
      min: 0,
      label: '内容',
      initVal: card.value || '',
      required: true
src/menu/components/card/cardcellcomponent/index.scss
@@ -16,10 +16,6 @@
    cursor: pointer;
  }
  .card-cell {
    border-style: solid;
    border-width: 0;
  }
  .card-button-cell {
    float: left;
    button {
src/menu/components/card/cardcomponent/index.jsx
@@ -213,21 +213,23 @@
    }
    return (
      <div className={'ant-col card-item ant-col-' + (card.setting.width || 6)} style={_style}>
        <CardCellComponent cards={cards} cardCell={card} side={side} elements={elements} updateElement={this.updateCard}/>
        <div className="card-control">
          <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
            <div className="mk-popover-control">
              <Icon className="plus" title="添加元素" onClick={this.addElement} type="plus" />
              <Icon className="plus" title="添加按钮" onClick={this.addButton} type="plus-square" />
              <Icon className="edit" type="edit" onClick={() => this.setState({settingVisible: true})} />
              <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
              <Icon className="close" title="删除卡片" type="delete" onClick={() => this.props.deleteElement(card)} />
              {card.setting.type === 'multi' ? <Switch size="small" onClick={this.changeSide} defaultChecked /> : null}
            </div>
          } trigger="hover">
            <Icon type="tool" />
          </Popover>
      <div className={'ant-col ant-col-' + (card.setting.width || 6)}>
        <div className="card-item" style={_style}>
          <CardCellComponent cards={cards} cardCell={card} side={side} elements={elements} updateElement={this.updateCard}/>
          <div className="card-control">
            <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
              <div className="mk-popover-control">
                <Icon className="plus" title="添加元素" onClick={this.addElement} type="plus" />
                <Icon className="plus" title="添加按钮" onClick={this.addButton} type="plus-square" />
                <Icon className="edit" type="edit" onClick={() => this.setState({settingVisible: true})} />
                <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
                {cards.subtype === 'propcard' ? <Icon className="close" title="删除卡片" type="delete" onClick={() => this.props.deleteElement(card)} /> : null}
                {card.setting.type === 'multi' ? <Switch size="small" onClick={this.changeSide} defaultChecked /> : null}
              </div>
            } trigger="hover">
              <Icon type="tool" />
            </Popover>
          </div>
        </div>
        <Modal
          wrapClassName="popview-modal"
src/menu/components/card/cardcomponent/settingform/index.jsx
@@ -10,6 +10,10 @@
    setting: PropTypes.object, // 数据源配置
  }
  state = {
    type: this.props.setting.type || 'simple'
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
@@ -70,14 +74,14 @@
                {getFieldDecorator('type', {
                  initialValue: setting.type || 'simple'
                })(
                  <Radio.Group>
                  <Radio.Group onChange={(e) => this.setState({ type: e.target.value })}>
                    <Radio value="simple">单卡</Radio>
                    <Radio value="multi">复式卡</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            <Col span={12}>
            {this.state.type === 'multi' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="复式卡片鼠标悬浮信息的动画效果。">
                  <Icon type="question-circle" />
@@ -93,7 +97,7 @@
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            </Col> : null}
          </Row>
        </Form>
      </div>
src/menu/components/card/data-card/index.jsx
@@ -67,7 +67,11 @@
        subcards = [{
          uuid: Utils.getuuid(),
          setting: { width: 6, type: 'simple'},
          style: {borderWidth: '1px', borderColor: '#e8e8e8', paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px'},
          style: {
            borderWidth: '1px', borderColor: '#e8e8e8',
            paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
            marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
          },
          backStyle: {},
          elements: [],
          backElements: []
src/menu/components/card/data-card/index.scss
@@ -5,9 +5,7 @@
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  border-style: solid;
  border-width: 0;
  min-height: 100px;
  min-height: 50px;
  
  .card-control {
    position: absolute;
@@ -27,15 +25,13 @@
    top: 1px;
    cursor: pointer;
    padding: 5px;
    background: #ffffff;
    background: rgba(255, 255, 255, 0.55);
  }
  .card-item {
    overflow-y: hidden;
    position: relative;
    min-height: 50px;
    border-style: solid;
    border-width: 0;
  }
  
  .card-item:hover {
src/menu/components/card/data-card/wrapsetting/index.jsx
@@ -50,6 +50,7 @@
  }
  render () {
    const { config } = this.props
    const { visible, dict, wrap } = this.state
    return (
@@ -69,6 +70,7 @@
          <SettingForm
            dict={dict}
            wrap={wrap}
            config={config}
            wrappedComponentRef={(inst) => this.verifyRef = inst}
          />
        </Modal>
src/menu/components/card/data-card/wrapsetting/settingform/index.jsx
@@ -6,8 +6,9 @@
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,    // 字典项
    wrap: PropTypes.object,    // 数据源配置
    dict: PropTypes.object,      // 字典项
    config: PropTypes.object,    // 卡片行信息
    wrap: PropTypes.object,      // 数据源配置
  }
  handleConfirm = () => {
@@ -24,7 +25,7 @@
  }
  render() {
    const { wrap } = this.props
    const { wrap, config } = this.props
    const { getFieldDecorator } = this.props.form
    const formItemLayout = {
@@ -78,7 +79,24 @@
                })(<InputNumber min={1} max={24} precision={0} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
            {config.subtype === 'propcard' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="选择静态值,无需配置数据源。">
                  <Icon type="question-circle" />
                  数据来源
                </Tooltip>
              }>
                {getFieldDecorator('datatype', {
                  initialValue: wrap.datatype || 'dynamic'
                })(
                  <Radio.Group>
                    <Radio value="dynamic">动态</Radio>
                    <Radio value="static">静态</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            {config.subtype === 'datacard' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="选择含有添加按钮时,请完善按钮配置信息。">
                  <Icon type="question-circle" />
@@ -94,7 +112,7 @@
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            </Col> : null}
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="选择卡片切换时,可向其他组件传递主键值。">
src/menu/components/card/prop-card/index.jsx
New file
@@ -0,0 +1,264 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import { Icon, Popover, Modal } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import MKEmitter from '@/utils/events.js'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import './index.scss'
const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
const WrapComponent = asyncIconComponent(() => import('../data-card/wrapsetting'))
const CardComponent = asyncComponent(() => import('../cardcomponent'))
const { confirm } = Modal
class antvBarLineChart extends Component {
  static propTpyes = {
    card: PropTypes.object,
    deletecomponent: PropTypes.func,
    updateConfig: PropTypes.func,
  }
  state = {
    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    back: false
  }
  UNSAFE_componentWillMount () {
    const { card, menu } = this.props
    if (card.isNew) {
      let dataName = ''
      if (card.floor === 1) {
        while (!dataName) {
          let _dataName = Utils.getdataName()
          if (menu.components.filter(com => com.dataName === _dataName).length === 0) {
            dataName = _dataName
          }
        }
      }
      let subcards = null
      if (card.config) {
        subcards = JSON.parse(card.config)
        subcards = subcards.map(scard => {
          scard.uuid = Utils.getuuid()
          scard.elements = scard.elements.map(elem => {
            elem.uuid = Utils.getuuid()
            return elem
          })
          scard.backElements = scard.backElements.map(elem => {
            elem.uuid = Utils.getuuid()
            return elem
          })
          return scard
        })
      } else {
        subcards = [{
          uuid: Utils.getuuid(),
          setting: { width: 6, type: 'simple'},
          style: {
            borderWidth: '1px', borderColor: '#e8e8e8',
            paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
            marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
          },
          backStyle: {},
          elements: [],
          backElements: []
        }]
      }
      let _card = {
        uuid: card.uuid,
        type: card.type,
        floor: card.floor,
        tabId: card.tabId || '',
        parentId: card.parentId || '',
        format: 'object',   // 组件属性 - 数据格式
        pageable: false,    // 组件属性 - 是否可分页
        switchable: true,  // 组件属性 - 数据是否可切换
        dataName: dataName,
        width: 24,
        name: card.name,
        subtype: card.subtype,
        setting: { interType: 'system' },
        wrap: { name: card.name, width: 24, addable: 'false', switch: 'false', datatype: 'dynamic' },
        style: { marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px' },
        columns: [],
        scripts: [],
        subcards: subcards
      }
      this.setState({
        card: _card
      })
      this.props.updateConfig(_card)
    } else {
      this.setState({
        card: fromJS(card).toJS()
      })
    }
  }
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
  }
  /**
   * @description 卡片行外层信息更新(数据源,样式等)
   */
  updateComponent = (component) => {
    this.setState({
      card: component
    })
    component.width = component.wrap.width
    component.name = component.wrap.name
    this.props.updateConfig(component)
  }
  /**
   * @description 单个卡片信息更新
   */
  updateCard = (cell) => {
    let card = fromJS(this.state.card).toJS()
    card.subcards = card.subcards.map(item => {
      if (item.uuid === cell.uuid) return cell
      return item
    })
    this.setState({card})
    this.props.updateConfig(card)
  }
  /**
   * @description 单个卡片信息更新
   */
  deleteCard = (cell) => {
    let card = fromJS(this.state.card).toJS()
    let _this = this
    confirm({
      content: '确定删除卡片吗?',
      onOk() {
        card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
        _this.setState({card})
        _this.props.updateConfig(card)
      },
      onCancel() {}
    })
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
  }
  getStyle = (comIds, style) => {
    const { card } = this.state
    if (comIds.length !== 1 || comIds[0] !== card.uuid) return
    let _card = {...card, style}
    this.setState({
      card: _card
    })
    this.props.updateConfig(_card)
  }
  addCard = () => {
    let card = fromJS(this.state.card).toJS()
    let newcard = {
      uuid: Utils.getuuid(),
      setting: { width: 6, type: 'simple'},
      style: {
        borderWidth: '1px', borderColor: '#e8e8e8',
        paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
        marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
      },
      backStyle: {},
      elements: [],
      backElements: []
    }
    if (card.subcards.length > 0) {
      newcard = fromJS(card.subcards[card.subcards.length - 1]).toJS()
      newcard.uuid = Utils.getuuid()
      newcard.elements = newcard.elements.map(elem => {
        elem.uuid = Utils.getuuid()
        return elem
      })
      newcard.backElements = newcard.backElements.map(elem => {
        elem.uuid = Utils.getuuid()
        return elem
      })
    }
    card.subcards.push(newcard)
    this.setState({card})
    this.props.updateConfig(card)
  }
  render() {
    const { card } = this.state
    return (
      <div className="menu-prop-card-edit-box" style={card.style}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加卡片" onClick={this.addCard} type="plus" />
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <Icon className="close" title="删除组件" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
            {card.wrap.datatype !== 'static' ? <SettingComponent config={card} updateConfig={this.updateComponent} /> : null}
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        {card.subcards.map(subcard => (<CardComponent key={subcard.uuid} cards={card} card={subcard} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
      </div>
    )
  }
}
const mapStateToProps = (state) => {
  return {
    menu: state.customMenu
  }
}
const mapDispatchToProps = () => {
  return {}
}
export default connect(mapStateToProps, mapDispatchToProps)(antvBarLineChart)
src/menu/components/card/prop-card/index.scss
New file
@@ -0,0 +1,72 @@
.menu-prop-card-edit-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 50px;
  .card-control {
    position: absolute;
    top: 0px;
    left: 0px;
    .anticon-tool {
      right: auto;
      left: 1px;
      padding: 1px;
    }
  }
  .anticon-tool {
    position: absolute;
    z-index: 1;
    font-size: 16px;
    right: 1px;
    top: 1px;
    cursor: pointer;
    padding: 5px;
    background: rgba(255, 255, 255, 0.55);
  }
  .card-item {
    overflow-y: hidden;
    position: relative;
    min-height: 50px;
  }
  .card-item:hover {
    box-shadow: 0px 0px 2px #e8e8e8;
  }
  .model-menu-card-cell-list .card-detail-row > .anticon-plus {
    position: absolute;
    right: -30px;
    font-size: 16px;
  }
  .model-menu-action-list {
    line-height: 40px;
    .ant-row > .anticon-plus {
      position: absolute;
      right: -30px;
      font-size: 16px;
    }
  }
  .card-add-button {
    text-align: right;
    clear: left;
    .anticon-plus {
      font-size: 20px;
      color: #26C281;
      padding: 5px;
      margin-right: 10px;
    }
  }
}
.menu-prop-card-edit-box::after {
  display: block;
  content: ' ';
  clear: both;
}
.menu-prop-card-edit-box:hover {
  box-shadow: 0px 0px 2px #e8e8e8;
}
src/menu/components/chart/antv-bar/index.scss
@@ -5,8 +5,6 @@
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  border-style: solid;
  border-width: 0;
  
  .canvas {
    margin: 0px;
@@ -32,7 +30,7 @@
      padding: 5px;
      cursor: pointer;
      color: rgba(0, 0, 0, 0.85);
      background: #ffffff;
      background: rgba(255, 255, 255, 0.55);
    }
    .chart-title {
src/menu/components/chart/antv-pie/index.scss
@@ -5,8 +5,6 @@
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  border-style: solid;
  border-width: 0;
  
  .canvas {
    margin: 0px;
@@ -32,7 +30,7 @@
      padding: 5px;
      cursor: pointer;
      color: rgba(0, 0, 0, 0.85);
      background: #ffffff;
      background: rgba(255, 255, 255, 0.55);
    }
    .chart-title {
src/menu/components/tabs/antv-tabs/index.scss
@@ -5,8 +5,6 @@
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  border-style: solid;
  border-width: 0;
  .ant-tabs-tabpane-active {
    min-height: 200px;
src/menu/components/tabs/tabcomponents/card.jsx
@@ -8,6 +8,7 @@
const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
  const originalIndex = findCard(id).index
@@ -47,6 +48,8 @@
      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}/>)
    }
  }
src/menu/menushell/card.jsx
@@ -8,6 +8,7 @@
const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
  const originalIndex = findCard(id).index
@@ -47,6 +48,8 @@
      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}/>)
    }
  }
src/menu/menushell/index.scss
@@ -1,6 +1,5 @@
.menu-shell-inner {
  min-height: calc(100vh - 150px);
  padding: 16px;
  min-height: calc(100vh - 100px);
  width: 100%;
  background-size: 100%;
@@ -15,4 +14,10 @@
  >.ant-empty {
    padding-top: 150px;
  }
  .anticon-tool {
    color: rgba(0, 0, 0, 0.55);
  }
  .anticon-tool:hover {
    color: #1890ff;
  }
}
src/menu/modelsource/option.jsx
@@ -17,7 +17,7 @@
export const menuOptions = [
  { type: 'menu', url: tabs, component: 'tabs', subtype: 'tabs', title: '标签页', width: 24 },
  { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '数据卡', config: `[{"uuid":"160135809128212dm7i29fim9ksto9od","setting":{"width":6},"style":{"paddingTop":"15px","marginTop":"4px","paddingRight":"15px","marginRight":"8px","marginLeft":"8px","backgroundColor":"rgba(255, 255, 255, 1)","borderColor":"#e8e8e8","paddingLeft":"15px","marginBottom":"4px","borderWidth":"1px","paddingBottom":"10px"},"backStyle":{},"elements":[{"datatype":"static","width":12,"marks":null,"height":1,"value":"关单","style":{},"prefix":"","postfix":"","format":"","eleType":"text","uuid":"160231860159931untbea62sgokunc5s"},{"datatype":"dynamic","width":12,"marks":null,"style":{"color":"rgba(250, 219, 20, 1)","textAlign":"right"},"btnstyle":{},"eleType":"icon","icon":"question-circle","field":"nvarchar2","uuid":"1602318768361nv8ql4t47sgcsn88b0u"},{"datatype":"static","width":24,"marks":null,"height":1,"value":"100","style":{"fontSize":"24px","fontWeight":"500","color":"rgba(0, 0, 0, 1)"},"prefix":"","btnstyle":{},"postfix":"","format":"","eleType":"text","uuid":"1602318817884v70gtgb65ubnm8mbcvv"},{"color":"#1890ff","width":24,"marks":null,"maxValue":100,"style":{"color":"rgba(250, 140, 22, 1)","paddingTop":"20px","paddingBottom":"10px"},"btnstyle":{},"eleType":"slider","field":"int1","uuid":"16023188871233rkktuvpp1h077igrsu"},{"eleType":"splitline","width":24,"color":"#e8e8e8","uuid":"1602320017038n31bk9o831ggug0tu0b","marks":null,"style":{"marginTop":"10px","marginBottom":"10px"},"btnstyle":{}},{"datatype":"static","width":12,"marks":null,"height":1,"value":"100","style":{"marginTop":"6px"},"prefix":"关单","btnstyle":{},"postfix":"","format":"","eleType":"text","uuid":"1602320061243drd7lf3agvn04kgr175"}],"backElements":[]}]` },
  { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '属性卡' },
  { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '属性卡', config: `[{"uuid":"1603681387259qaqf1127f72esmtchge","setting":{"width":6,"type":"simple"},"style":{"paddingTop":"15px","marginTop":"8px","paddingRight":"15px","marginRight":"8px","marginLeft":"8px","borderColor":"#e8e8e8","paddingLeft":"15px","marginBottom":"8px","borderWidth":"1px","paddingBottom":"15px"},"backStyle":{},"elements":[{"datatype":"static","width":12,"marks":null,"height":1,"value":"超时工单","style":{"color":"rgba(67, 67, 67, 0.51)"},"prefix":"","postfix":"","format":"","eleType":"text","uuid":"1603681402945qnkgm7q8cng65evn5ev"},{"eleType":"icon","datatype":"static","width":12,"icon":"question-circle","tooltip":"超时工单","uuid":"1603681473384i2crkbtofg4pu76k06a","marks":null,"style":{"textAlign":"right","color":"rgba(250, 219, 20, 1)"}},{"datatype":"static","width":12,"marks":null,"height":1,"value":"100","style":{"fontSize":"24px","color":"rgba(0, 0, 0, 1)"},"prefix":"","postfix":"","format":"","eleType":"number","uuid":"1603681539870d704ufqf98kc6t7537t"},{"color":"rgba(250, 219, 20, 1)","datatype":"static","width":24,"marks":null,"maxValue":100,"value":50,"style":{"paddingTop":"10px","paddingBottom":"10px"},"eleType":"slider","uuid":"1603683067556mvupau0odvrtv45u7o8"},{"eleType":"splitline","width":24,"color":"#e8e8e8","uuid":"1603683117981t9k55k8an430fuppmci","marks":null,"style":{"paddingTop":"5px","paddingBottom":"5px"}},{"datatype":"static","width":12,"marks":null,"height":1,"value":"100","style":{"color":"rgba(0, 0, 0, 0.65)","marginTop":"10px"},"prefix":"超时工单  ","postfix":"","format":"","eleType":"text","uuid":"1603683136553uvsmkfohkft9idbfkhu"}],"backElements":[]}]` },
  { type: 'menu', url: line, component: 'line', subtype: 'line', title: '折线图' },
  { type: 'menu', url: line1, component: 'line', subtype: 'line1', title: '阶梯折线图' },
  { type: 'menu', url: bar, component: 'bar', subtype: 'bar', title: '柱状图' },
src/menu/padcontroller/index.jsx
New file
@@ -0,0 +1,97 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Form, Col, Icon } from 'antd'
import zhCN from '@/locales/zh-CN/mob.js'
import enUS from '@/locales/en-US/mob.js'
import StyleInput from '../stylecontroller/styleInput'
import './index.scss'
class MobController extends Component {
  static propTpyes = {
    config: PropTypes.any,
    updateConfig: PropTypes.func,
  }
  state = {
    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    paddingTop: '',
    paddingBottom: '',
    paddingLeft: '',
    paddingRight: ''
  }
  UNSAFE_componentWillMount () {
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 修改背景颜色 ,颜色控件
   */
  changePadding = (val, type) => {
    let config = fromJS(this.props.config).toJS()
    config.style[type] = val
    this.props.updateConfig(config)
  }
  render () {
    const { config } = this.props
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 6 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 18 }
      }
    }
    return (
      <div className="menu-padding-controller">
        <Form {...formItemLayout}>
          <Col span={12}>
            <Form.Item
              colon={false}
              label={<Icon title="上边距" type="arrow-up"/>}
            >
              <StyleInput defaultValue={config.style.paddingTop || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingTop')}/>
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item
              colon={false}
              label={<Icon title="下边距" type="arrow-down"/>}
            >
              <StyleInput defaultValue={config.style.paddingBottom || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingBottom')}/>
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item
              colon={false}
              label={<Icon title="左边距" type="arrow-left"/>}
            >
              <StyleInput defaultValue={config.style.paddingLeft || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingLeft')}/>
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item
              colon={false}
              label={<Icon title="右边距" type="arrow-right"/>}
            >
              <StyleInput defaultValue={config.style.paddingRight || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingRight')}/>
            </Form.Item>
          </Col>
        </Form>
      </div>
    )
  }
}
export default MobController
src/menu/padcontroller/index.scss
New file
@@ -0,0 +1,9 @@
.menu-padding-controller {
  width: 100%;
  height: 100%;
  overflow: hidden;
  .ant-form-item label > .anticon {
    font-size: 16px;
    vertical-align: middle;
  }
}
src/menu/stylecontroller/index.jsx
@@ -309,7 +309,7 @@
              {options.includes('font') ? <Panel header="字体" key="font">
                <Col span={12}>
                  <Form.Item colon={false} label={<Icon title="字体大小" type="font-size" />}>
                    <InputNumber defaultValue={card.fontSize} min={12} max={100} precision={0} onChange={this.changeFontSize} />
                    <InputNumber defaultValue={card.fontSize || 14} min={12} max={100} precision={0} onChange={this.changeFontSize} />
                  </Form.Item>
                </Col>
                <Col span={12}>
@@ -333,12 +333,12 @@
                </Col>
                <Col span={12}>
                  <Form.Item colon={false} label={<Icon title="行高" type="line-height" />}>
                    <InputNumber defaultValue={card.lineHeight} min={1} max={10} precision={1} onChange={this.changeLineHeight} />
                    <InputNumber defaultValue={card.lineHeight || 1.5} min={1} max={10} precision={1} onChange={this.changeLineHeight} />
                  </Form.Item>
                </Col>
                <Col span={12}>
                  <Form.Item colon={false} label={<Icon title="字间距" type="column-width" />}>
                    <InputNumber defaultValue={card.letterSpacing} min={0} max={100} precision={0} onChange={this.changeLetterSpacing}/>
                    <InputNumber defaultValue={card.letterSpacing || 0} min={0} max={100} precision={0} onChange={this.changeLetterSpacing}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
@@ -472,11 +472,11 @@
                    label={<Icon title="边框宽度" type="column-width" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    {borposition === 'outer' ? <StyleInput defaultValue={card.borderWidth || ''} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'left' ? <StyleInput defaultValue={card.borderLeftWidth || ''} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'right' ? <StyleInput defaultValue={card.borderRightWidth || ''} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'top' ? <StyleInput defaultValue={card.borderTopWidth || ''} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'bottom' ? <StyleInput defaultValue={card.borderBottomWidth || ''} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'outer' ? <StyleInput defaultValue={card.borderWidth || '0px'} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'left' ? <StyleInput defaultValue={card.borderLeftWidth || '0px'} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'right' ? <StyleInput defaultValue={card.borderRightWidth || '0px'} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'top' ? <StyleInput defaultValue={card.borderTopWidth || '0px'} options={['px']} onChange={this.changeBorderWidth}/> : null}
                    {borposition === 'bottom' ? <StyleInput defaultValue={card.borderBottomWidth || '0px'} options={['px']} onChange={this.changeBorderWidth}/> : null}
                  </Form.Item>
                </Col>
                <Col span={24}>
@@ -508,7 +508,7 @@
                    colon={false}
                    label={<Icon title="上边距" type="arrow-up"/>}
                  >
                    <StyleInput defaultValue={card.marginTop} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginTop')}/>
                    <StyleInput defaultValue={card.marginTop || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginTop')}/>
                  </Form.Item>
                </Col>
                <Col span={12}>
@@ -516,7 +516,7 @@
                    colon={false}
                    label={<Icon title="下边距" type="arrow-down"/>}
                  >
                    <StyleInput defaultValue={card.marginBottom} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginBottom')}/>
                    <StyleInput defaultValue={card.marginBottom || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginBottom')}/>
                  </Form.Item>
                </Col>
                <Col span={12}>
@@ -524,7 +524,7 @@
                    colon={false}
                    label={<Icon title="左边距" type="arrow-left"/>}
                  >
                    <StyleInput defaultValue={card.marginLeft} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginLeft')}/>
                    <StyleInput defaultValue={card.marginLeft || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginLeft')}/>
                  </Form.Item>
                </Col>
                <Col span={12}>
@@ -532,7 +532,7 @@
                    colon={false}
                    label={<Icon title="右边距" type="arrow-right"/>}
                  >
                    <StyleInput defaultValue={card.marginRight} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginRight')}/>
                    <StyleInput defaultValue={card.marginRight || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginRight')}/>
                  </Form.Item>
                </Col>
              </Panel> : null}
@@ -542,7 +542,7 @@
                    colon={false}
                    label={<Icon title="上边距" type="arrow-up"/>}
                  >
                    <StyleInput defaultValue={card.paddingTop} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingTop')}/>
                    <StyleInput defaultValue={card.paddingTop || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingTop')}/>
                  </Form.Item>
                </Col>
                <Col span={12}>
@@ -550,7 +550,7 @@
                    colon={false}
                    label={<Icon title="下边距" type="arrow-down"/>}
                  >
                    <StyleInput defaultValue={card.paddingBottom} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingBottom')}/>
                    <StyleInput defaultValue={card.paddingBottom || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingBottom')}/>
                  </Form.Item>
                </Col>
                <Col span={12}>
@@ -558,7 +558,7 @@
                    colon={false}
                    label={<Icon title="左边距" type="arrow-left"/>}
                  >
                    <StyleInput defaultValue={card.paddingLeft} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingLeft')}/>
                    <StyleInput defaultValue={card.paddingLeft || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingLeft')}/>
                  </Form.Item>
                </Col>
                <Col span={12}>
@@ -566,7 +566,7 @@
                    colon={false}
                    label={<Icon title="右边距" type="arrow-right"/>}
                  >
                    <StyleInput defaultValue={card.paddingRight} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingRight')}/>
                    <StyleInput defaultValue={card.paddingRight || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingRight')}/>
                  </Form.Item>
                </Col>
              </Panel> : null}
src/tabviews/custom/components/card/cardcellList/index.jsx
@@ -88,7 +88,9 @@
    } else if (card.eleType === 'slider') {
      let val = 0
      if (data.hasOwnProperty(card.field)) {
      if (card.datatype === 'static') {
        val = card.value
      } else if (data.hasOwnProperty(card.field)) {
        val = parseFloat(data[card.field])
        if (isNaN(val)) {
          val = 0
src/tabviews/custom/components/card/data-card/index.jsx
@@ -29,7 +29,6 @@
    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, // 字典
    config: null,              // 图表配置信息
    pageIndex: 1,
    empty: false,
    loading: false,            // 数据加载状态
    data: null,                // 数据
    total: null
@@ -124,7 +123,7 @@
  }
  render() {
    const { config, empty, loading, data, pageIndex, total } = this.state
    const { config, loading, data, pageIndex, total } = this.state
    let _total = config.setting.pageSize * pageIndex
    let pageable = config.pageable && config.setting.laypage
@@ -147,7 +146,7 @@
          ))}
        </div> : null}
        {pageable ? <div className={'prev-page ' + (total <= _total ? 'disabled' : '')} onClick={this.nextPage}><div><div><img src={nextImg} alt=""/></div></div></div> : null}
        {empty ? <Empty description={false}/> : null}
        {data && data.length === 0 ? <Empty description={false}/> : null}
      </div>
    )
  }
src/tabviews/custom/components/card/data-card/index.scss
@@ -3,7 +3,7 @@
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 100px;
  min-height: 50px;
  display: flex;
  position: relative;
@@ -39,7 +39,6 @@
    flex: 10;
  }
  .card-item-box {
    border-style: solid;
    .card-cell-list::after {
      content: ' ';
      display: block;
@@ -102,9 +101,9 @@
  }
  .ant-empty {
    position: absolute;
    top: calc(50% - 34px);
    left: calc(50% - 92px);
    width: 100%;
    min-height: 100px;
    padding-top: 15px;
    .ant-empty-image {
      height: 60px;
src/tabviews/custom/components/card/prop-card/asyncButtonComponent.jsx
New file
@@ -0,0 +1,34 @@
import React, {Component} from 'react'
import { Button } from 'antd'
/**
 * @description 异步加载模块
 * @param {*} importComponent
 */
export default function asyncComponent(importComponent) {
  return class extends Component {
    constructor(props) {
      super(props)
      this.state = {
        component: null
      }
    }
    async componentDidMount() {
      const {default: component} = await importComponent()
      this.setState({component})
    }
    // <Button className="loading-skeleton" disabled={true}></Button> // 骨架按钮
    render() {
      const C = this.state.component
      const btn = this.props.btn || {}
      return C ?
        <C {...this.props} /> :
        <Button icon={btn.OpenType === 'excelOut' ? 'download' : 'upload'} disabled={true} title={btn.label} style={{border: 0, background: 'transparent'}}></Button>
    }
  }
}
src/tabviews/custom/components/card/prop-card/index.jsx
New file
@@ -0,0 +1,124 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, notification } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
import zhCN from '@/locales/zh-CN/main.js'
import enUS from '@/locales/en-US/main.js'
import './index.scss'
const CardItem = asyncComponent(() => import('../cardItem'))
class DataCard extends Component {
  static propTpyes = {
    BID: PropTypes.any,              // 父级Id
    data: PropTypes.array,           // 统一查询数据
    config: PropTypes.object,        // 组件配置信息
    mainSearch: PropTypes.any,       // 全局搜索条件
    menuType: PropTypes.any,         // 菜单类型
    dataManager: PropTypes.any,      // 数据权限
  }
  state = {
    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, // 字典
    config: null,              // 图表配置信息
    loading: true,             // 数据加载状态
    data: null,                // 数据
  }
  UNSAFE_componentWillMount () {
    let _config = fromJS(this.props.config).toJS()
    this.setState({
      config: _config,
      arr_field: _config.columns.map(col => col.field).join(','),
    }, () => {
      this.loadData()
    })
  }
  /**
   * @description 校验图表的按钮组,如果为统计图表,计算图表字段
   */
  componentDidMount () {
  }
  /**
   * @description 图表数据更新,刷新内容
   */
  UNSAFE_componentWillReceiveProps (nextProps) {
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  async loadData () {
    const { mainSearch, BID, menuType, dataManager } = this.props
    const { config, arr_field } = this.state
    let searches = []
    if (mainSearch && mainSearch.length > 0) { // 主表搜索条件
      searches = [...mainSearch, ...searches]
    }
    if (config.wrap.datatype === 'static') {
      this.setState({
        loading: false,
        data: []
      })
      return
    } else {
      this.setState({
        loading: true
      })
    }
    let _orderBy = config.setting.order || ''
    let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, 1, 1, BID, menuType, dataManager)
    let result = await Api.genericInterface(param)
    if (result.status) {
      this.setState({
        data: result.data,
        loading: false
      })
    } else {
      this.setState({
        loading: false
      })
      notification.error({
        top: 92,
        message: result.message,
        duration: 10
      })
    }
  }
  render() {
    const { config, loading, data } = this.state
    return (
      <div className="custom-card-box" style={config.style}>
        {loading ?
          <div className="loading-mask">
            <div className="ant-spin-blur"></div>
            <Spin />
          </div> : null
        }
        {data ? <div className="card-row-list">
          {config.subcards.map((item, index) => (
            <CardItem key={index} card={item} cards={config} data={data[0] || {}} />
          ))}
        </div> : null}
      </div>
    )
  }
}
export default DataCard
src/tabviews/custom/components/card/prop-card/index.scss
New file
@@ -0,0 +1,106 @@
.custom-card-box {
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 50px;
  display: flex;
  position: relative;
  .card-row-list::after {
    content: ' ';
    display: block;
    clear: both;
  }
  .card-row-list {
    flex: 10;
  }
  .card-item-box {
    .card-cell-list::after {
      content: ' ';
      display: block;
      clear: both;
    }
  }
  .card-cell-list {
    position: relative;
    .ant-mk-slider {
      box-sizing: border-box;
      margin: 0;
      color: rgba(0, 0, 0, 0.65);
      font-size: 14px;
      font-variant: tabular-nums;
      line-height: 1.5;
      list-style: none;
      font-feature-settings: 'tnum', "tnum";
      position: relative;
      height: 12px;
      padding: 3px 0;
      cursor: pointer;
      touch-action: none;
      .ant-mk-slider-track {
        height: 7px;
        position: absolute;
        background-color: #91d5ff;
        border-radius: 4px;
        transition: background-color 0.3s;
      }
      .ant-mk-slider-rail {
        height: 7px;
        position: absolute;
        width: 100%;
        background-color: #f5f5f5;
        border-radius: 2px;
        transition: background-color 0.3s;
      }
      .ant-mk-slider-handle {
        position: absolute;
        width: 14px;
        height: 14px;
        margin-top: -4px;
        margin-left: -7px;
        background-color: #fff;
        border: solid 2px #91d5ff;
        border-radius: 50%;
        cursor: pointer;
        transition: border-color 0.3s, box-shadow 0.6s, transform 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28), -webkit-box-shadow 0.6s, -webkit-transform 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28);
      }
    }
    .ant-mk-splitline {
      height: 1px;
    }
    .ant-slider {
      margin: 0px;
    }
  }
  .loading-mask {
    position: absolute;
    left: 40px;
    top: 0;
    right: 40px;
    bottom: 0px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: justify;
    z-index: 1;
    .ant-spin-blur {
      position: absolute;
      width: 100%;
      height: 100%;
      opacity: 0.5;
      background: #ffffff;
    }
  }
}
.custom-card-box::after {
  content: ' ';
  display: block;
  clear: both;
}
src/tabviews/custom/components/chart/antv-bar-line/index.scss
@@ -3,8 +3,6 @@
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  border-style: solid;
  border-width: 0;
  min-height: 100px;
  > .chart-header {
src/tabviews/custom/components/chart/antv-pie/index.scss
@@ -3,8 +3,6 @@
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  border-style: solid;
  border-width: 0;
  min-height: 100px;
  > .chart-header {
src/tabviews/custom/components/tabs/antv-tabs/index.scss
@@ -5,6 +5,4 @@
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  border-style: solid;
  border-width: 0;
}
src/tabviews/custom/index.jsx
@@ -21,6 +21,7 @@
const AntvPie = asyncSpinComponent(() => import('./components/chart/antv-pie'))
const AntvTabs = asyncSpinComponent(() => import('./components/tabs/antv-tabs'))
const DataCard = asyncSpinComponent(() => import('./components/card/data-card'))
const PropCard = asyncSpinComponent(() => import('./components/card/prop-card'))
class CustomPage extends Component {
  static propTpyes = {
@@ -176,7 +177,7 @@
        }
      }
      if (!component.format) return component // 没有动态数据  数据格式 array 或 object
      if (!component.format || (component.subtype === 'propcard' && component.wrap.datatype === 'static')) return component // 没有动态数据  数据格式 array 或 object
      let _customScript = ''
      component.scripts && component.scripts.forEach(script => {
@@ -465,6 +466,12 @@
              <DataCard config={item} BID={BID} mainSearch={mainSearch} menuType={menuType} dataManager={dataManager} />
            </Col>
          )
        } else if (item.subtype === 'propcard') {
          return (
            <Col span={item.width} key={item.uuid}>
              <PropCard config={item} data={data} BID={BID} mainSearch={mainSearch} menuType={menuType} dataManager={dataManager} />
            </Col>
          )
        } else {
          return null
        }
src/utils/events.js
@@ -1,5 +1,6 @@
import EventEmitter from 'events'
class MKEmitter extends EventEmitter {}
const Emitter = new EventEmitter()
Emitter.setMaxListeners(1000)
export default new MKEmitter()
export default Emitter
src/views/menudesign/index.jsx
@@ -26,7 +26,8 @@
const MenuForm = asyncComponent(() => import('@/menu/menuform'))
const SourceWrap = asyncComponent(() => import('@/menu/modelsource'))
const MenuShell = asyncComponent(() => import('@/menu/menushell'))
const Controller = asyncComponent(() => import('@/menu/bgcontroller'))
const BgController = asyncComponent(() => import('@/menu/bgcontroller'))
const PaddingController = asyncComponent(() => import('@/menu/padcontroller'))
const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
const ModalController = asyncComponent(() => import('@/menu/modalconfig/controller'))
const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
@@ -235,7 +236,10 @@
            MenuNo: MenuNo,
            tables: [],
            components: [],
            style: {backgroundColor: '#ffffff', backgroundImage: ''}
            style: {
              backgroundColor: '#ffffff', backgroundImage: '',
              paddingTop: '16px', paddingBottom: '80px', paddingLeft: '16px', paddingRight: '16px'
            }
          }
        } else {
          config.uuid = MenuId
@@ -338,7 +342,10 @@
    }
    config.components.forEach(item => {
      if (!error && item.setting) {
      if (error) return
      if (item.subtype === 'propcard' && item.wrap.datatype === 'static') return
      if (item.setting) {
        if (item.setting.interType === 'system' && item.setting.execute !== 'false' && !item.setting.dataresource) {
          error = `组件《${item.name}》未设置数据源!`
        } else if (item.setting.interType === 'system' && item.setting.execute === 'false' && item.scripts.length === 0) {
@@ -347,7 +354,7 @@
          error = `组件《${item.name}》未设置主键!`
        }
      }
      if (!error && (item.type === 'bar' || item.type === 'line' || item.type === 'pie')) {
      if (item.type === 'bar' || item.type === 'line' || item.type === 'pie') {
        if (!item.plot.Xaxis) {
          error = `组件《${item.name}》图表字段尚未设置!`
        }
@@ -422,7 +429,10 @@
                    <SourceWrap />
                  </Panel>
                  <Panel header={'背景'} key="background">
                    {config ? <Controller config={config} updateConfig={this.updateConfig} /> : null}
                    {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
                  </Panel>
                  <Panel header={'内边距'} key="padding">
                    {config ? <PaddingController config={config} updateConfig={this.updateConfig} /> : null}
                  </Panel>
                </Collapse>
              </div>