king
2020-09-26 ab60d53b67f802878662aaa5a5b52580cca421b8
2020-09-26
27个文件已修改
7个文件已添加
1535 ■■■■ 已修改文件
src/assets/css/main.scss 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/demo1.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/demo2.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/demo3.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/demo4.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/demo5.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/actioncomponent/actionform/index.jsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/actioncomponent/dragaction/index.jsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/actioncomponent/formconfig.jsx 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/actioncomponent/index.jsx 118 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/card.jsx 60 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/index.jsx 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/index.scss 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/elementform/index.jsx 77 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/formconfig.jsx 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.jsx 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.scss 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.jsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.scss 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/wrapsetting/settingform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/index.jsx 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/antv-tabs/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/antv-tabs/index.scss 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/settingform/index.jsx 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/searchcomponent/dragsearch/index.jsx 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/searchcomponent/index.jsx 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/searchcomponent/searchform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/index.jsx 491 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/index.scss 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/colorsketch/index.jsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/customform/index.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/index.jsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.jsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/main.scss
@@ -307,6 +307,9 @@
      .profile {
        color: purple;
      }
      .style {
        color: orange;
      }
      .model-datasource {
        display: inline-block;
        position: unset;
src/assets/img/demo1.jpg
src/assets/img/demo2.jpg
src/assets/img/demo3.jpg
src/assets/img/demo4.jpg
src/assets/img/demo5.jpg
src/menu/actioncomponent/actionform/index.jsx
@@ -10,10 +10,10 @@
const { TextArea } = Input
const actionTypeOptions = {
  pop: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError'],
  prompt: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError'],
  exec: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError'],
  excelIn: ['label', 'Ot', 'OpenType', 'intertype', 'show', 'icon', 'class', 'sheet', 'execSuccess', 'execError'],
  pop: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'syncComponent'],
  prompt: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'syncComponent'],
  exec: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'syncComponent'],
  excelIn: ['label', 'Ot', 'OpenType', 'intertype', 'show', 'icon', 'class', 'sheet', 'execSuccess', 'execError', 'syncComponent'],
  excelOut: ['label', 'OpenType', 'intertype', 'show', 'icon', 'class', 'execSuccess', 'execError', 'pagination', 'search'],
  popview: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'popClose'],
  tab: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'linkmenu'],
@@ -547,6 +547,18 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'mcascader') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal
              })(
                <Cascader options={item.options} expandTrigger="hover" placeholder="" />
              )}
            </Form.Item>
          </Col>
        )
      }
    })
    return fields
src/menu/actioncomponent/dragaction/index.jsx
@@ -8,7 +8,7 @@
import Card from './card'
import './index.scss'
const Container = ({type, list, handleList, handleMenu, deleteMenu, profileMenu, doubleClickCard }) => {
const Container = ({plus, list, handleList, handleMenu, deleteMenu, profileMenu, doubleClickCard }) => {
  const [cards, setCards] = useState(list)
  const moveCard = (id, atIndex) => {
    const { card, index } = findCard(id)
@@ -116,10 +116,6 @@
    newcard.errorTime = 10
    newcard.verify = null
    newcard.show = 'icon'
    if (type === 'chart') {
      newcard.show = 'icon'
    }
    
    let targetId = cards.length > 0 ? cards[cards.length - 1].uuid : 0
@@ -150,7 +146,7 @@
          doubleClickCard={doubleClickBtn}
        />
      ))}
      <Icon type="plus" title="添加按钮" onClick={addaction}/>
      {plus !== 'false' ? <Icon type="plus" title="添加按钮" onClick={addaction}/> : null}
    </div>
  )
}
src/menu/actioncomponent/formconfig.jsx
@@ -11,7 +11,7 @@
 * @param {*} permFuncField  存储过程可用的开始字段
 * @param {*} type           按钮类型,用于区分可选的打开方式
 */
export function getActionForm (card, functip, setting, permFuncField = [], type, menulist = []) {
export function getActionForm (card, functip, setting, permFuncField = [], type, menulist = [], modules = []) {
  let opentypes = [
    {
      value: 'pop',
@@ -43,23 +43,7 @@
    }
  ]
  if (card.intertype === 'inner' && !card.innerFunc) {
    card.intertype = 'system'
  }
  if (type === 'chart') {
    if (card.focus) {
      // 导入和导出excel,按钮名称直接为导入、导出
      card.label = '导出Excel'
      card.OpenType = 'excelOut'
      card.icon = 'download'
      card.intertype = setting.interType
      card.innerFunc = setting.innerFunc
      card.sysInterface = setting.sysInterface
      card.outerFunc = setting.outerFunc
      card.interface = setting.interface
      card.class = 'dgreen'
    }
    opentypes = [
      {
        value: 'excelIn',
@@ -389,6 +373,14 @@
        text: '非必填'
      }]
    },
    {
      type: 'mcascader',
      key: 'syncComponent',
      label: '同步刷新',
      initVal: card.syncComponent || [],
      required: false,
      options: modules
    },
    // {
    //   type: 'select',
    //   key: 'linkcomponents',
src/menu/actioncomponent/index.jsx
@@ -13,6 +13,7 @@
import enUS from '@/locales/en-US/model.js'
import { getActionForm } from './formconfig'
import MKEmitter from '@/utils/events.js'
import ActionForm from './actionform'
import VerifyCard from '@/templates/zshare/verifycard'
import CreateFunc from '@/templates/zshare/createfunc'
@@ -27,6 +28,7 @@
class ActionComponent extends Component {
  static propTpyes = {
    type: PropTypes.string,          // 菜单类型,主表或子表
    plus: PropTypes.any,             // 是否存在添加按钮,值为false时隐藏
    config: PropTypes.object,        // 菜单配置信息
    setSubConfig: PropTypes.func,    // 设置子配置信息
    updateaction: PropTypes.func     // 菜单配置更新
@@ -51,6 +53,10 @@
    })
  }
  componentDidMount () {
    MKEmitter.addListener('addButton', this.addButton)
  }
  /**
   * @description 监听到按钮复制时,触发按钮编辑
   */
@@ -60,6 +66,29 @@
    if (!is(fromJS(nextProps.config.action), fromJS(this.props.config.action)) && !is(fromJS(nextProps.config.action), fromJS(actionlist))) {
      this.setState({actionlist: fromJS(nextProps.config.action).toJS()})
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props.config), fromJS(nextProps.config)) || !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('addButton', this.addButton)
  }
  addButton = (cardId, element) => {
    if (cardId !== this.props.config.uuid) return
    const { actionlist } = this.state
    this.setState({actionlist: [...actionlist, element]})
    this.handleAction(element)
  }
  /**
@@ -76,6 +105,57 @@
        this.props.updateaction({...config, action: list})
      })
    }
  }
  getModules = (components, selfId) => {
    let modules = components.map(item => {
      if (item.uuid === selfId) {
        return {
          children: null
        }
      } else if (item.format) {
        return {
          value: item.uuid,
          label: item.name
        }
      } else if (item.type === 'tabs') {
        let _item = {
          value: item.uuid,
          label: item.name,
          children: item.subtabs.map(f_tab => {
            let subItem = {
              value: f_tab.uuid,
              label: f_tab.label,
              children: this.getModules(f_tab.components, selfId)
            }
            if (!subItem.children || subItem.children.length === 0) {
              return {children: null}
            }
            return subItem
          })
        }
        _item.children = _item.children.filter(t => t.children !== null)
        if (_item.children.length === 0) {
          return {children: null}
        }
        return _item
      } else {
        return {
          children: null
        }
      }
    })
    modules = modules.filter(mod => mod.children !== null)
    if (modules.length === 0) {
      return null
    }
    return modules
  }
  /**
@@ -101,22 +181,7 @@
      })
    }
    // let modules = []
    // menu.components.forEach(item => {
    //   if (item.uuid === config.uuid) return
    //   modules.push({
    //     value: item.uuid,
    //     text: item.setting.name
    //   })
    // })
    // if (supModule && supModule !== 'empty') {
    //   if (modules.filter(item => item.value === supModule).length === 0) {
    //     supModule = ''
    //   }
    // }
    let modules = this.getModules(menu.components, config.uuid)
    if (menu.fstMenuList && card.linkmenu && card.linkmenu.length > 0) {
      let _param = {
@@ -165,14 +230,14 @@
        this.setState({
          visible: true,
          card: card,
          formlist: getActionForm(card, functip, config.setting, menu.permFuncField, this.props.type, menulist)
          formlist: getActionForm(card, functip, config.setting, menu.permFuncField, this.props.type, menulist, modules)
        })
      })
    } else {
      this.setState({
        visible: true,
        card: card,
        formlist: getActionForm(card, functip, config.setting, menu.permFuncField, this.props.type, menulist)
        formlist: getActionForm(card, functip, config.setting, menu.permFuncField, this.props.type, menulist, modules)
      })
    }
  }
@@ -875,27 +940,14 @@
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props.config), fromJS(nextProps.config)) || !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  render() {
    const { config, type } = this.props
    const { config, plus } = this.props
    const { actionlist, visible, card, dict, copying, profVisible } = this.state
    return (
      <div className="model-menu-action-list">
        <DragElement
          type={type}
          plus={plus}
          list={actionlist}
          handleList={this.handleList}
          handleMenu={this.handleAction}
src/menu/components/card/cardcellcomponent/dragaction/card.jsx
@@ -1,7 +1,13 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Icon, Popover, Slider } from 'antd'
import { Icon, Popover } from 'antd'
import './index.scss'
import demo1 from '@/assets/img/demo1.jpg'
import demo2 from '@/assets/img/demo2.jpg'
import demo3 from '@/assets/img/demo3.jpg'
import demo4 from '@/assets/img/demo4.jpg'
import demo5 from '@/assets/img/demo5.jpg'
const Card = ({ id, cardIds, card, moveCard, findCard, editCard, delCard }) => {
  const originalIndex = findCard(id).index
@@ -33,21 +39,61 @@
  if (card.padding) {
    _style.padding = card.padding
  }
  if (card.fontSize) {
    _style.fontSize = card.fontSize + 'px'
  }
  if (card.fontWeight) {
    _style.fontWeight = card.fontWeight
  }
  const getContent = () => {
    if (card.eleType === 'text' || card.eleType === 'number') {
      return card.value
      let val = `${card.prefix}${card.value}${card.postfix}`
      return val
    } else if (card.eleType === 'icon') {
      return (<Icon type={card.icon}/>)
    } else if (card.eleType === 'slider') {
      return (
        <div className="ant-slider">
          <div className="ant-slider-rail"></div>
          <div className="ant-slider-track" style={{width: '30%', backgroundColor: card.color}}></div>
          <div className="ant-slider-handle" style={{left: '30%', borderColor: card.color}}></div>
          <div style={{display: 'none'}}><Slider value={30} tooltipVisible={false} /></div>
        <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>
      )
    } else if (card.eleType === 'picture') {
      let _imagestyle = {}
      if (card.url) {
        _imagestyle = {backgroundImage: `url('${card.url}')`}
      } else {
        let index = card.uuid.match(/\d{1}/g)
        index = index[index.length - 1] % 5
        let demos = [demo1, demo2, demo3, demo4, demo5]
        _imagestyle = {backgroundImage: `url('${demos[index]}')`}
      }
      if (card.radius === 'true') {
        _imagestyle.borderRadius = '50%'
      }
      if (card.lenWidRadio === '16:9') {
        _imagestyle.paddingTop = '56.25%'
      } else if (card.lenWidRadio === '3:2') {
        _imagestyle.paddingTop = '66.67%'
      } else if (card.lenWidRadio === '4:3') {
        _imagestyle.paddingTop = '75%'
      } else {
        _imagestyle.paddingTop = '100%'
      }
      return (
        <div className="ant-mk-picture" style={_imagestyle}></div>
      )
    } else if (card.eleType === 'splitline') {
      return (
        <div className="ant-mk-splitline" style={{backgroundColor: card.color}}></div>
      )
    }
  }
src/menu/components/card/cardcellcomponent/dragaction/index.jsx
@@ -2,13 +2,11 @@
import { useDrop } from 'react-dnd'
import { is, fromJS } from 'immutable'
import update from 'immutability-helper'
import { Icon } from 'antd'
import Utils from '@/utils/utils.js'
import Card from './card'
import './index.scss'
const Container = ({type, list, handleList, handleMenu, deleteMenu }) => {
const Container = ({list, handleList, handleMenu, deleteMenu }) => {
  const [cards, setCards] = useState(list)
  const moveCard = (id, atIndex) => {
    const { card, index } = findCard(id)
@@ -45,29 +43,6 @@
    drop() {}
  })
  const addelement = () => {
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.eleType = 'text'
    newcard.datatype = 'dynamic'
    newcard.color = 'rgba(0,0,0,0.85)'
    newcard.padding = '5px'
    newcard.align = 'left'
    let targetId = cards.length > 0 ? cards[cards.length - 1].uuid : 0
    const { index: overIndex } = findCard(`${targetId}`)
    let targetIndex = overIndex
    targetIndex++
    const _cards = update(cards, { $splice: [[targetIndex, 0, newcard]] })
    handleList(_cards, newcard)
  }
  return (
    <div ref={drop} className="ant-row card-detail-row">
      {cards.map(card => (
@@ -82,7 +57,6 @@
          findCard={findCard}
        />
      ))}
      <Icon type="plus" title="添加元素" onClick={addelement}/>
    </div>
  )
}
src/menu/components/card/cardcellcomponent/dragaction/index.scss
@@ -1,32 +1,53 @@
.card-detail-row {
  .ant-slider-track {
    height: 7px;
  }
  .ant-slider-rail {
    height: 7px;
  }
  .cyan {
    .ant-slider-track {
      background-color: #13C2C2;
  .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-slider-handle {
      border-color: #13C2C2;
    .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);
    }
  }
  .orange {
    .ant-slider-track {
      background-color: orange;
    }
    .ant-slider-handle {
      border-color: lightsalmon;
    }
  .ant-mk-splitline {
    height: 1px;
  }
  .primary {
    .ant-slider-track {
      background-color: #1890ff;
    }
    .ant-slider-handle {
      border-color: #91d5ff;
    }
  .ant-mk-picture {
    background-size: cover;
    background-position: center center;
    background-repeat: no-repeat;
  }
}
src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -1,19 +1,20 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { fromJS } from 'immutable'
import { Form, Row, Col, Input, Select, Icon, Radio, Tooltip, InputNumber } from 'antd'
import { formRule } from '@/utils/option.js'
import { Form, Row, Col, Input, Select, Icon, Radio, Tooltip, InputNumber, notification } from 'antd'
import { formRule } from '@/utils/option.js'
import FileUpload from '@/tabviews/zshare/fileupload'
import ColorSketch from '@/mob/colorsketch'
import './index.scss'
const cardTypeOptions = {
  text: ['eleType', 'datatype', 'value', 'format', 'fontSize', 'fontWeight', 'width', 'height', 'color', 'align', 'padding', 'prefix', 'postfix'],
  number: ['eleType', 'datatype', 'value', 'format', 'fontSize', 'fontWeight', 'width', 'height', 'color', 'align', 'padding', 'prefix', 'postfix'],
  picture: ['eleType', 'datatype', 'width', 'lenWidRadio', 'radius', 'padding'],
  picture: ['eleType', 'datatype', 'width', 'lenWidRadio', 'radius', 'padding', 'url'],
  icon: ['eleType', 'icon', 'fontSize', 'width', 'height', 'color', 'align', 'padding', 'tooltip'],
  slider: ['eleType', 'field', 'width', 'sildercolor', 'padding'],
  splitline: ['eleType', 'color', 'padding'],
  slider: ['eleType', 'field', 'width', 'color', 'padding', 'maxValue'],
  splitline: ['eleType', 'color', 'width', 'padding'],
}
class MainSearch extends Component {
@@ -92,6 +93,7 @@
      
      let _formlist = this.state.formlist.map(item => {
        item.hidden = !_options.includes(item.key)
        if (item.key === 'field') {
          item.options = []
          config.columns.forEach(col => {
@@ -116,10 +118,10 @@
        eleType: value,
        formlist: _formlist
      }, () => {
        if (value === 'slider' || value === 'splitline') {
          if (this.props.form.getFieldValue('width') !== undefined) {
            this.props.form.setFieldsValue({width: 24})
          }
        if (value === 'slider') {
          this.props.form.setFieldsValue({width: 24, color: '#1890ff'})
        } else if (value === 'splitline') {
          this.props.form.setFieldsValue({width: 24, color: '#e8e8e8'})
        }
      })
    } else if (key === 'field') {
@@ -135,7 +137,9 @@
    if (key === 'datatype') {
      let _options = this.getOptions(eleType, value)
      this.setState({
        datatype: value,
        formlist: this.state.formlist.map(item => {
          item.hidden = !_options.includes(item.key)
@@ -212,7 +216,7 @@
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<InputNumber min={0} max={10000} precision={0} />)}
              })(<InputNumber min={item.min || 0} max={item.max || 10000} precision={item.precision || 0} />)}
            </Form.Item>
          </Col>
        )
@@ -294,6 +298,35 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'file') {
        let filelist = []
        if (item.initVal) {
          filelist = [{
            uid: `1`,
            name: item.initVal.slice(item.initVal.lastIndexOf('/') + 1),
            status: 'done',
            url: item.initVal,
            origin: true
          }]
        }
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: filelist,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <FileUpload maxFile={item.maxfile} fileType={'text'} />
              )}
            </Form.Item>
          </Col>
        )
      }
    })
    return fields
@@ -306,6 +339,30 @@
        if (!err) {
          values.uuid = this.props.card.uuid
          values.marks = this.props.card.marks || null
          if (values.url) {
            if (values.url.length > 0) {
              if (values.url[0].origin && values.url[0].url) {
                values.url = values.url[0].url
              } else if (!values.url[0].origin && values.url[0].status === 'done' && values.url[0].response) {
                values.url = values.url[0].response
              } else {
                values.url = ''
              }
            } else {
              values.url = ''
            }
          }
          if (values.eleType === 'picture' && values.datatype === 'static' && !values.url) {
            notification.warning({
              top: 92,
              message: '尚未添加图片或图片上传失败,请重新添加!',
              duration: 5
            })
            return
          }
          resolve(values)
        } else {
          reject(err)
src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -47,6 +47,14 @@
      options: []
    },
    {
      type: 'file',
      key: 'url',
      label: '图片',
      initVal: card.url || '',
      maxfile: 1,
      required: false
    },
    {
      type: 'text',
      key: 'value',
      label: '内容',
@@ -182,6 +190,15 @@
    },
    {
      type: 'number',
      key: 'maxValue',
      min: 1,
      label: '最大值',
      initVal: card.maxValue || 100,
      tooltip: '即进度为100%时的值。',
      required: true,
    },
    {
      type: 'number',
      key: 'width',
      min: 1,
      max: 24,
@@ -218,7 +235,7 @@
      key: 'radius',
      label: '圆角',
      initVal: card.radius || 'false',
      required: true,
      required: false,
      options: [
        { value: 'true', text: '有' },
        { value: 'false', text: '无' }
src/menu/components/card/cardcellcomponent/index.jsx
@@ -7,6 +7,7 @@
import enUS from '@/locales/en-US/model.js'
import { getCardCellForm } from './formconfig'
import MKEmitter from '@/utils/events.js'
import ElementForm from './elementform'
import DragElement from './dragaction'
import './index.scss'
@@ -38,20 +39,42 @@
    })
  }
  componentDidMount () {
    MKEmitter.addListener('cardAddElement', this.cardAddElement)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props.config), fromJS(nextProps.config)) || !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 按钮顺序调整,或拖拽添加
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  handleList = (list, card) => {
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('cardAddElement', this.cardAddElement)
  }
  cardAddElement = (cardId, element) => {
    if (cardId !== this.props.config.uuid) return
    const { elements } = this.state
    this.setState({elements: [...elements, element]})
    this.handleElement(element)
  }
  /**
   * @description 按钮顺序调整
   */
  handleList = (list) => {
    const { config } = this.props
    if (card) {
      this.setState({elements: list})
      this.handleElement(card)
    } else {
      this.setState({elements: list}, () => {
        this.props.updateElement({...config, elements: list})
      })
    }
    this.setState({elements: list}, () => {
      this.props.updateElement({...config, elements: list})
    })
  }
  /**
@@ -132,20 +155,6 @@
      },
      onCancel() {}
    })
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props.config), fromJS(nextProps.config)) || !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  render() {
src/menu/components/card/cardcellcomponent/index.scss
@@ -1,5 +1,4 @@
.model-menu-card-cell-list {
  min-height: calc(100% - 50px);
  position: relative;
  .ant-form-item-label {
    .anticon-question-circle {
@@ -16,9 +15,7 @@
    padding: 5px;
    cursor: pointer;
  }
  // .card-cell {
  //   box-sizing: content-box;
  // }
  .card-cell:hover {
    box-shadow: 0px 0px 1px #d8d8d8;
  }
src/menu/components/card/data-card/index.jsx
@@ -7,6 +7,7 @@
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'
@@ -58,7 +59,7 @@
        name: card.name,
        subtype: card.subtype,
        setting: { interType: 'system' },
        wrap: { name: card.name, width: 24, height: 200, cardWidth: 6, addable: 'false', switch: 'false' },
        wrap: { name: card.name, width: 24, cardWidth: 6, addable: 'false', switch: 'false' },
        columns: [],
        scripts: [],
        action: [],
@@ -103,6 +104,53 @@
    this.props.updateConfig(component)
  }
  addElement = () => {
    const { card } = this.state
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.eleType = 'text'
    newcard.datatype = 'dynamic'
    newcard.color = 'rgba(0,0,0,0.85)'
    newcard.padding = '5px'
    newcard.align = 'left'
    // 注册事件-添加元素
    MKEmitter.emit('cardAddElement', card.uuid, newcard)
  }
  addButton = () => {
    const { card } = this.state
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.label = 'button'
    newcard.sqlType = ''
    newcard.Ot = 'requiredSgl'
    newcard.OpenType = 'prompt'
    newcard.icon = ''
    newcard.class = 'default'
    newcard.intertype = 'system'
    newcard.method = 'POST'
    newcard.execSuccess = 'grid'
    newcard.execError = 'never'
    newcard.popClose = 'never'
    newcard.errorTime = 10
    newcard.verify = null
    newcard.show = 'link'
    // 注册事件-添加元素
    MKEmitter.emit('addButton', card.uuid, newcard)
  }
  changeStyle = () => {
    // MKEmitter.emit('changeStyle', card.uuid, newcard)
  }
  render() {
    const { card } = this.state
@@ -119,8 +167,20 @@
        </Popover>
        <div className={'ant-col card-item ant-col-' + (card.wrap.cardWidth || 6)} style={{height: card.wrap.height ? card.wrap.height + 'px' : 'auto'}}>
          <CardCellComponent config={card} updateElement={this.updateComponent}/>
          <ActionComponent config={card} updateaction={this.updateComponent}/>
          <ActionComponent plus="false" config={card} updateaction={this.updateComponent}/>
          <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="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
              </div>
            } trigger="hover">
              <Icon type="tool" />
            </Popover>
          </div>
        </div>
        {card.wrap.addable === 'true' ? <div className="card-add-button"><Icon type="plus" /></div> : null}
      </div>
    )
  }
src/menu/components/card/data-card/index.scss
@@ -3,6 +3,18 @@
  box-sizing: border-box;
  background: #ffffff;
  
  .card-control {
    position: absolute;
    top: 0px;
    right: 0px;
    width: 26px;
    height: 27px;
    z-index: 1;
    background: #ffffff;
    .anticon-tool {
      padding: 5px;
    }
  }
  .anticon-tool {
    position: absolute;
    z-index: 1;
@@ -14,6 +26,7 @@
  }
  .card-item {
    overflow-y: hidden;
    position: relative;
    min-height: 50px;
    border: 1px solid #e8e8e8;
@@ -29,7 +42,6 @@
    font-size: 16px;
  }
  .model-menu-action-list {
    min-height: 40px;
    line-height: 40px;
    .ant-row > .anticon-plus {
      position: absolute;
@@ -37,6 +49,16 @@
      font-size: 16px;
    }
  }
  .card-add-button {
    text-align: right;
    clear: left;
    .anticon-plus {
      font-size: 20px;
      color: #26C281;
      padding: 5px;
      margin-right: 10px;
    }
  }
}
.menu-data-card-edit-box::after {
  display: block;
src/menu/components/card/data-card/wrapsetting/settingform/index.jsx
@@ -82,7 +82,7 @@
              <Form.Item label={
                <Tooltip placement="topLeft" title="高度为空时,使用自适应高度。">
                  <Icon type="question-circle" />
                  高度
                  卡片高度
                </Tooltip>
              }>
                {getFieldDecorator('height', {
src/menu/components/chart/antv-bar/index.jsx
@@ -587,6 +587,57 @@
    this.props.updateConfig(component)
  }
  addSearch = () => {
    const { card } = this.state
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.label = 'label'
    newcard.initval = ''
    newcard.type = 'select'
    newcard.resourceType = '0'
    newcard.options = []
    newcard.setAll = 'false'
    newcard.orderType = 'asc'
    newcard.display = 'dropdown'
    newcard.match = '='
    // 注册事件-添加搜索
    MKEmitter.emit('addSearch', card.uuid, newcard)
  }
  addButton = () => {
    const { card } = this.state
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.label = '导出Excel'
    newcard.sqlType = ''
    newcard.Ot = 'requiredSgl'
    newcard.OpenType = 'excelOut'
    newcard.icon = 'download'
    newcard.class = 'dgreen'
    newcard.intertype = card.setting.interType
    newcard.innerFunc = card.setting.innerFunc || ''
    newcard.sysInterface = card.setting.sysInterface || ''
    newcard.outerFunc = card.setting.outerFunc || ''
    newcard.interface = card.setting.interface || ''
    newcard.method = 'POST'
    newcard.execSuccess = 'grid'
    newcard.execError = 'never'
    newcard.popClose = 'never'
    newcard.errorTime = 10
    newcard.verify = null
    newcard.show = 'icon'
    // 注册事件-添加按钮
    MKEmitter.emit('addButton', card.uuid, newcard)
  }
  render() {
    const { card } = this.state
@@ -600,6 +651,8 @@
          />
          <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
            <div className="mk-popover-control">
              <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" />
              <Icon className="plus" title="添加按钮" onClick={this.addButton} type="plus-square" />
              <ChartCompileForm config={card} dict={this.state.dict} plotchange={this.updateComponent}/>
              <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
              <SettingComponent config={card} updateConfig={this.updateComponent}/>
@@ -610,8 +663,8 @@
        </div>
        <ActionComponent
          type="chart"
          plus="false"
          config={card}
          tabs={[]}
          // setSubConfig={(_btn) => this.setSubConfig(_btn, 'button')}
          updateaction={this.updateComponent}
        />
src/menu/components/tabs/antv-tabs/index.jsx
@@ -227,7 +227,7 @@
          <TabPane disabled tab={
            <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
              <div className="mk-popover-control">
                <Icon className="plus" title="add" type="plus" onClick={this.tabAdd} />
                <Icon className="plus" title="添加标签" type="plus" onClick={this.tabAdd} />
                <SettingComponent config={tabs} updateConfig={this.updateComponent} />
                <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(tabs.uuid)} />
              </div>
src/menu/components/tabs/antv-tabs/index.scss
@@ -6,9 +6,6 @@
  .ant-tabs-tabpane-active {
    min-height: 200px;
  }
  .ant-tabs-bar {
    margin-bottom: 0px;
  }
  .ant-tabs .ant-tabs-left-bar .ant-tabs-tab {
    padding: 0px;
src/menu/datasource/verifycard/settingform/index.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, Tooltip, Icon, notification, Select, InputNumber } from 'antd'
import { Form, Row, Col, Input, Radio, Tooltip, Icon, notification, Select, InputNumber, Cascader } from 'antd'
import { formRule } from '@/utils/option.js'
import Utils from '@/utils/utils.js'
@@ -13,37 +13,81 @@
    menu: PropTypes.any,          // 菜单配置信息
    config: PropTypes.object,     // 组件配置
    setting: PropTypes.object,    // 数据源配置
    modules: PropTypes.array,     // 列设置
    columns: PropTypes.array,     // 列设置
    scripts: PropTypes.array,     // 自定义脚本
  }
  state = {
    interType: this.props.setting.interType || 'system',
    supModule: '',
    modules: []
  }
  UNSAFE_componentWillMount () {
    const { menu, setting, config } = this.props
    let supModule = setting.supModule || ''
    let modules = []
    const { menu, config } = this.props
    menu.components.forEach(item => {
      if (!item.switchable || !item.setting || !item.setting.name || item.uuid === config.uuid) return
      modules.push({
        value: item.uuid,
        text: item.setting.name
      })
    })
    if (supModule && supModule !== 'empty') {
      if (modules.filter(item => item.value === supModule).length === 0) {
        supModule = ''
      }
    let modules = this.getModules(menu.components, config.uuid)
    if (!modules) {
      modules = []
    }
    this.setState({supModule, modules})
    modules.unshift({
      value: 'empty',
      label: '无'
    })
    this.setState({modules})
  }
  getModules = (components, selfId) => {
    let modules = components.map(item => {
      if (item.uuid === selfId) {
        return {
          children: null
        }
      } else if (item.switchable) {
        return {
          value: item.uuid,
          label: item.name
        }
      } else if (item.type === 'tabs') {
        let _item = {
          value: item.uuid,
          label: item.name,
          children: item.subtabs.map(f_tab => {
            let subItem = {
              value: f_tab.uuid,
              label: f_tab.label,
              children: this.getModules(f_tab.components, selfId)
            }
            if (!subItem.children || subItem.children.length === 0) {
              return {children: null}
            }
            return subItem
          })
        }
        _item.children = _item.children.filter(t => t.children !== null)
        if (_item.children.length === 0) {
          return {children: null}
        }
        return _item
      } else {
        return {
          children: null
        }
      }
    })
    modules = modules.filter(mod => mod.children !== null)
    if (modules.length === 0) {
      return null
    }
    return modules
  }
  handleConfirm = () => {
@@ -129,7 +173,7 @@
  render() {
    const { setting, menu, columns, config } = this.props
    const { getFieldDecorator } = this.props.form
    const { interType, supModule, modules } = this.state
    const { interType, modules } = this.state
    const formItemLayout = {
      labelCol: {
@@ -310,7 +354,7 @@
                </Tooltip>
              }>
                {getFieldDecorator('supModule', {
                  initialValue: supModule,
                  initialValue: setting.supModule || [],
                  rules: [
                    {
                      required: true,
@@ -318,14 +362,7 @@
                    }
                  ]
                })(
                  <Select>
                    <Select.Option key="empty" value="empty"> 无 </Select.Option>
                    {modules.map((option, i) =>
                      <Select.Option key={i} value={option.value}>
                        {option.text}
                      </Select.Option>
                    )}
                  </Select>
                  <Cascader options={modules} expandTrigger="hover" placeholder="" />
                )}
              </Form.Item>
            </Col>
src/menu/searchcomponent/dragsearch/index.jsx
@@ -2,7 +2,7 @@
import { useDrop } from 'react-dnd'
import { is, fromJS } from 'immutable'
import update from 'immutability-helper'
import { Col, Icon } from 'antd'
import { Col } from 'antd'
import Utils from '@/utils/utils.js'
import Card from './card'
import './index.scss'
@@ -80,34 +80,34 @@
    drop() {}
  })
  const addsearch = (e) => {
    e.stopPropagation()
  // const addsearch = (e) => {
  //   e.stopPropagation()
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
  //   let newcard = {}
  //   newcard.uuid = Utils.getuuid()
  //   newcard.focus = true
    newcard.label = 'label'
    newcard.initval = ''
    newcard.type = 'select'
    newcard.resourceType = '0'
    newcard.options = []
    newcard.setAll = 'false'
    newcard.orderType = 'asc'
    newcard.display = 'dropdown'
    newcard.match = '='
  //   newcard.label = 'label'
  //   newcard.initval = ''
  //   newcard.type = 'select'
  //   newcard.resourceType = '0'
  //   newcard.options = []
  //   newcard.setAll = 'false'
  //   newcard.orderType = 'asc'
  //   newcard.display = 'dropdown'
  //   newcard.match = '='
    
    let targetId = cards.length > 0 ? cards[cards.length - 1].uuid : 0
  //   let targetId = cards.length > 0 ? cards[cards.length - 1].uuid : 0
    const { index: overIndex } = findCard(`${targetId}`)
    let targetIndex = overIndex
  //   const { index: overIndex } = findCard(`${targetId}`)
  //   let targetIndex = overIndex
    targetIndex++
  //   targetIndex++
    const _cards = update(cards, { $splice: [[targetIndex, 0, newcard]] })
  //   const _cards = update(cards, { $splice: [[targetIndex, 0, newcard]] })
    handleList(_cards, newcard)
  }
  //   handleList(_cards, newcard)
  // }
  return (
    <div ref={drop} className="ant-row">
@@ -125,7 +125,7 @@
          />
        </Col>
      ))}
      <Icon type="plus" onClick={addsearch}/>
      {/* <Icon type="plus" onClick={addsearch}/> */}
    </div>
  )
}
src/menu/searchcomponent/index.jsx
@@ -5,6 +5,7 @@
import { Modal, notification } from 'antd'
import moment from 'moment'
import MKEmitter from '@/utils/events.js'
import Api from '@/api'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
@@ -41,6 +42,10 @@
    })
  }
  componentDidMount () {
    MKEmitter.addListener('addSearch', this.addSearch)
  }
  /**
   * @description 监听到搜索条件复制时,触发搜索条件编辑
   */
@@ -50,6 +55,29 @@
    if (!is(fromJS(nextProps.config.search), fromJS(this.props.config.search)) && !is(fromJS(nextProps.config.search), fromJS(searchlist))) {
      this.setState({searchlist: fromJS(nextProps.config.search).toJS()})
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('addSearch', this.addSearch)
  }
  addSearch = (cardId, element) => {
    if (cardId !== this.props.config.uuid) return
    const { searchlist } = this.state
    this.setState({searchlist: [...searchlist, element]})
    this.handleSearch(element)
  }
  /**
@@ -255,19 +283,6 @@
      },
      onCancel() {}
    })
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  render() {
src/menu/searchcomponent/searchform/index.jsx
@@ -505,7 +505,7 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'cascader') { // 多选
      } else if (item.type === 'cascader') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
src/menu/stylecontroller/index.jsx
New file
@@ -0,0 +1,491 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Collapse, Form, Input, Col, Icon, InputNumber, Select, Radio, Popover, Menu, Drawer } from 'antd'
import zhCN from '@/locales/zh-CN/mob.js'
import enUS from '@/locales/en-US/mob.js'
import ColorSketch from '@/mob/colorsketch'
import FileUpload from '@/tabviews/zshare/fileupload'
import './index.scss'
const { Panel } = Collapse
const { Option } = Select
class MobController extends Component {
  static propTpyes = {
    editElem: PropTypes.any,
    updateStyle: PropTypes.func,
  }
  state = {
    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    fontColor: '#000000',
    backgroundColor: '#ffffff',
    bgimages: [],
    marginTop: '',
    marginTopVal: '',
    marginBottom: '',
    marginBottomVal: '',
  }
  UNSAFE_componentWillReceiveProps (nextProps) {
    if (!is(fromJS(this.props.editElem), fromJS(nextProps.editElem))) {
      this.setState({
        card: null
      }, () => {
        if (!nextProps.editElem) return
        let _card = fromJS(nextProps.editElem).toJS()
        let bgImg = _card.backgroundImage || ''
        if (bgImg && /^linear-gradient/.test(bgImg)) {
          bgImg = bgImg.replace('linear-gradient(', '')
          bgImg = bgImg.replace(')', '')
        } else if (bgImg && /^url/.test(bgImg)) {
          bgImg = bgImg.replace('url(', '')
          bgImg = bgImg.replace(')', '')
        }
        this.setState({
          card: _card,
          fontColor: _card.color || '#000000',
          backgroundColor: _card.backgroundColor || '#ffffff',
          backgroundImage: bgImg,
          marginTop: _card.marginTop ? _card.marginTop : '',
          marginTopVal: _card.marginTop ? parseInt(_card.marginTop) : '',
          marginBottom: _card.marginBottom ? _card.marginBottom : '',
          marginBottomVal: _card.marginBottomVal ? parseInt(_card.marginBottomVal) : ''
        })
      })
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  updateStyle = (style) => {
    const { card } = this.state
    this.props.updateStyle({componentId: card.componentId, classId: card.classId, uuid: card.uuid, style})
  }
  /**
   * @description 字体大小切换,超出范围忽略
   */
  changeFontSize = (val) => {
    let value = parseInt(val)
    if (isNaN(value) || value < 12 || value > 100) return
    this.updateStyle({fontSize: `${value}px`})
  }
  /**
   * @description 修改行间距,超出范围忽略
   */
  changeLineHeight = (val) => {
    let value = parseFloat(val)
    if (isNaN(value) || value < 1 || value > 10) return
    this.updateStyle({lineHeight: value})
  }
  /**
   * @description 字体间距修改,超出范围忽略
   */
  changeLetterSpacing = (val) => {
    let value = parseFloat(val)
    if (isNaN(value) || value < 0 || value > 100) return
    this.updateStyle({letterSpacing: `${value}px`})
  }
  /**
   * @description 修改字体粗细
   */
  boldChange = (val) => {
    this.updateStyle({fontWeight: val})
  }
  /**
   * @description 修改字体颜色 ,颜色控件
   */
  changeFontColor = (val) => {
    this.setState({
      fontColor: val
    })
    this.updateStyle({color: val})
  }
  /**
   * @description 修改字体颜色 ,手动输入
   */
  changeFontColorInput = (e) => {
    this.setState({
      fontColor: e.target.value
    })
  }
  /**
   * @description 字体对齐
   */
  changeTextAlign = (e) => {
    this.updateStyle({textAlign: e.target.value})
  }
  /**
   * @description 字体样式,倾斜
   */
  changeFontStyle = (e) => {
    this.updateStyle({fontStyle: e.target.value})
  }
  /**
   * @description 字体装饰,下划线、贯穿线、上划线
   */
  changeTextDecoration = (e) => {
    this.updateStyle({textDecoration: e.target.value})
  }
  /**
   * @description 修改背景颜色 ,颜色控件
   */
  changeBackgroundColor = (val) => {
    this.setState({
      backgroundColor: val
    })
    this.updateStyle({backgroundColor: val})
  }
  /**
   * @description 修改字体颜色 ,手动输入
   */
  changeBackgroundColorInput = (e) => {
    this.setState({
      backgroundColor: e.target.value
    })
  }
  imgChange = (list) => {
    if (list[0] && list[0].response) {
      this.setState({
        bgimages: [],
        backgroundImage: list[0].response
      })
      this.updateStyle({backgroundImage: `url(${list[0].response})`})
    } else {
      this.setState({bgimages: list})
    }
  }
  changeBackgroundImageInput = (e) => {
    this.setState({
      backgroundImage: e.target.value
    })
  }
  submitBackgroundImage = (e) => {
    let val = e.target.value
    val = val.replace(/^\s*|\s*$/ig, '')
    if (/^http|^\/\//.test(val)) {
      val = `url(${val})`
    } else if (/^#|,/ig.test(val)) {
      val = `linear-gradient(${val})`
    }
    this.updateStyle({backgroundImage: val})
  }
  submitBorder = (val, type) => {
    this.updateStyle({[type]: val})
  }
  changeBorderRadius = (val) => {
    let value = parseFloat(val)
    if (isNaN(value) || value < 0 || value > 500) return
    this.updateStyle({borderRadius: `${value}px`})
  }
  changeMarginTop = (e) => {
    let val = e.target.value
    let _val = parseInt(val)
    this.setState({
      marginTop: val
    })
    if (isNaN(_val)) return
    this.setState({
      marginTopVal: _val
    })
  }
  submitMarginTop = (val) => {
    this.setState({
      marginTop: val
    })
    this.updateStyle({marginTop: val})
  }
  changeMarginBottom = (e) => {
    let val = e.target.value
    let _val = parseInt(val)
    this.setState({
      marginBottom: val
    })
    if (isNaN(_val)) return
    this.setState({
      marginBottomVal: _val
    })
  }
  submitMarginBottom = (val) => {
    this.setState({
      marginBottom: val
    })
    this.updateStyle({marginBottom: val})
  }
  render () {
    const { card, fontColor, backgroundColor, backgroundImage, bgimages, marginTop, marginTopVal, marginBottom, marginBottomVal } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Drawer
        title="样式修改"
        placement="left"
        width="300"
        closable={true}
        maskStyle={{opacity: 0.2}}
        visible={this.state.visible}
      >
        <div className="mob-controller">
          <Form {...formItemLayout}>
            {card ? <Collapse expandIconPosition="right" defaultActiveKey={card.items[0]} accordion={true}>
              {card.items.includes('font') ? <Panel header="字体" key="font">
                <Col span={12}>
                  <Form.Item colon={false} label={<Icon title="字体大小" type="font-size" />}>
                    <InputNumber defaultValue={card.fontSize || 14} min={12} max={100} precision={0} onChange={this.changeFontSize} />
                  </Form.Item>
                </Col>
                <Col span={12}>
                  <Form.Item colon={false} label={<Icon title="字体粗细" type="bold" />}>
                    <Select defaultValue={card.fontWeight || 'normal'} onChange={this.boldChange}>
                      <Option value="normal">normal</Option>
                      <Option value="bold">bold</Option>
                      <Option value="bolder">bolder</Option>
                      <Option value="lighter">lighter</Option>
                      <Option value="100">100</Option>
                      <Option value="200">200</Option>
                      <Option value="300">300</Option>
                      <Option value="400">400</Option>
                      <Option value="500">500</Option>
                      <Option value="600">600</Option>
                      <Option value="700">700</Option>
                      <Option value="800">800</Option>
                      <Option value="900">900</Option>
                    </Select>
                  </Form.Item>
                </Col>
                <Col span={12}>
                  <Form.Item colon={false} label={<Icon title="行间距" type="line-height" />}>
                    <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 || 0} min={0} max={100} precision={0} onChange={this.changeLetterSpacing}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="字体颜色" type="font-colors" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <ColorSketch value={card.color || '#000000'} onChange={this.changeFontColor} />
                    <Input value={fontColor} onChange={this.changeFontColorInput} />
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={' '}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Radio.Group defaultValue={card.fontStyle || 'normal'} onChange={this.changeFontStyle}>
                      <Radio.Button value="normal"><span title="标准">N</span></Radio.Button>
                      <Radio.Button value="italic"><Icon title="斜体" type="italic" /></Radio.Button>
                      <Radio.Button value="oblique" style={{fontStyle: 'oblique'}}><span title="倾斜">B</span></Radio.Button>
                    </Radio.Group>
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={' '}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Radio.Group className="text-align" defaultValue={card.textAlign || 'left'} onChange={this.changeTextAlign}>
                      <Radio.Button value="left"><Icon title="左对齐" type="align-left" /></Radio.Button>
                      <Radio.Button value="center"><Icon title="居中对齐" type="align-center" /></Radio.Button>
                      <Radio.Button value="right"><Icon title="右对齐" type="align-right" /></Radio.Button>
                    </Radio.Group>
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={' '}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Radio.Group className="text-decoration" defaultValue={card.textDecoration || 'none'} onChange={this.changeTextDecoration}>
                      <Radio.Button value="none"><span title="标准">N</span></Radio.Button>
                      <Radio.Button value="underline"><Icon title="下划线" type="underline" /></Radio.Button>
                      <Radio.Button value="line-through"><Icon title="中划线" type="strikethrough" /></Radio.Button>
                      <Radio.Button value="overline" style={{textDecoration: 'overline'}}><span title="上划线">O</span></Radio.Button>
                    </Radio.Group>
                  </Form.Item>
                </Col>
              </Panel> : null}
              {card.items.includes('background') ? <Panel header="背景" key="background">
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="背景颜色" type="bg-colors" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <ColorSketch color={card.backgroundColor || '#ffffff'} changeColor={this.changeBackgroundColor} />
                    <Input value={backgroundColor} onChange={this.changeBackgroundColorInput} />
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="背景图片" type="picture" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <FileUpload value={bgimages} maxFile={2} fileType="text" onChange={this.imgChange}/>
                    <Input placeholder="" value={backgroundImage} autoComplete="off" onBlur={this.submitBackgroundImage} onPressEnter={this.submitBackgroundImage} onChange={this.changeBackgroundImageInput} />
                  </Form.Item>
                </Col>
              </Panel> : null}
              {card.items.includes('border') ? <Panel header="边框" key="border">
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="外边框" type="border-outer" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Input placeholder="" defaultValue={card.border || ''} autoComplete="off" onBlur={(e) => this.submitBorder(e.target.value, 'border')} onPressEnter={(e) => this.submitBorder(e.target.value, 'border')}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="左边框" type="border-left" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Input placeholder="" defaultValue={card.borderLeft || ''} autoComplete="off" onBlur={(e) => this.submitBorder(e.target.value, 'borderLeft')} onPressEnter={(e) => this.submitBorder(e.target.value, 'borderLeft')}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="右边框" type="border-right" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Input placeholder="" defaultValue={card.borderRight || ''} autoComplete="off" onBlur={(e) => this.submitBorder(e.target.value, 'borderRight')} onPressEnter={(e) => this.submitBorder(e.target.value, 'borderRight')}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="上边框" type="border-top" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Input placeholder="" defaultValue={card.borderTop || ''} autoComplete="off" onBlur={(e) => this.submitBorder(e.target.value, 'borderTop')} onPressEnter={(e) => this.submitBorder(e.target.value, 'borderTop')}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="下边框" type="border-bottom" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Input placeholder="" defaultValue={card.borderBottom || ''} autoComplete="off" onBlur={(e) => this.submitBorder(e.target.value, 'borderBottom')} onPressEnter={(e) => this.submitBorder(e.target.value, 'borderBottom')}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="圆角" type="radius-setting" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <InputNumber defaultValue={card.borderRadius || 0} min={0} max={500} precision={0} onChange={this.changeBorderRadius}/>
                  </Form.Item>
                </Col>
              </Panel> : null}
              {card.items.includes('margin') ? <Panel header="外边距" key="margin">
                <Col span={12}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="上边距" type="vertical-align-top"/>}
                  >
                    <Popover placement="bottom" overlayClassName="margin-popover" content={
                      marginTopVal !== '' ?
                      <Menu>
                        <Menu.Item onClick={() => this.submitMarginTop(`${marginTopVal}px`)}>{marginTopVal} px</Menu.Item>
                        <Menu.Item onClick={() => this.submitMarginTop(`${marginTopVal}vh`)}>{marginTopVal} vh</Menu.Item>
                      </Menu> : null
                    } trigger="hover">
                      <Input value={marginTop} onChange={this.changeMarginTop}/>
                    </Popover>
                  </Form.Item>
                </Col>
                <Col span={12}>
                  <Form.Item
                    colon={false}
                    label={<Icon title="下边距" type="vertical-align-bottom"/>}
                  >
                    <Popover placement="bottom" overlayClassName="margin-popover" content={
                      marginBottomVal !== '' ?
                      <Menu>
                        <Menu.Item onClick={() => this.submitMarginBottom(`${marginBottomVal}px`)}>{marginBottomVal} px</Menu.Item>
                        <Menu.Item onClick={() => this.submitMarginBottom(`${marginBottomVal}vh`)}>{marginBottomVal} vh</Menu.Item>
                      </Menu> : null
                    } trigger="hover">
                      <Input value={marginBottom} onChange={this.changeMarginBottom}/>
                    </Popover>
                  </Form.Item>
                </Col>
              </Panel> : null}
            </Collapse> : null}
          </Form>
        </div>
      </Drawer>
    )
  }
}
export default MobController
src/menu/stylecontroller/index.scss
New file
@@ -0,0 +1,147 @@
.mob-controller {
  width: 100%;
  height: 100%;
  overflow: hidden;
  >.ant-form >.ant-collapse {
    border: 0;
    background: #262E3F;
    > .ant-collapse-item {
      border-color: #202735;
      > .ant-collapse-header {
        padding: 10px 40px 10px 16px;
        background: #262E3F;
        color: rgba(255, 255, 255, 0.85);
      }
      >.ant-collapse-content {
        color: rgba(255, 255, 255, 0.85);
        background-color: #202735;
        border-top: 1px solid #202735;
        .ant-input-number {
          width: 100%;
        }
        .ant-form-item {
          margin-bottom: 5px;
          .ant-form-item-label > label {
            color: rgba(255, 255, 255, 0.85);
            .anticon {
              vertical-align: middle;
              font-size: 18px;
            }
          }
          .ant-form-item-control-wrapper  {
            .ant-form-item-control {
              input {
                background: transparent;
                color: rgba(255, 255, 255, 0.65);
              }
              .ant-input {
                height: 28px;
              }
              .ant-input-number {
                height: 28px;
                background: transparent;
                .ant-input-number-input {
                  height: 28px;
                }
              }
              .ant-select-selection {
                background: transparent;
                color: rgba(255, 255, 255, 0.65);
              }
              .color-sketch-block {
                position: relative;
                top: 10px;
              }
              .color-sketch-block + .ant-input {
                float: right;
                width: 70%;
                margin-top: 9px;
                padding-right: 5px;
                padding-left: 5px;
              }
              .ant-select-arrow {
                color: inherit;
              }
              .ant-input-number-handler-wrap {
                background: transparent;
                .ant-input-number-handler {
                  color: rgba(255, 255, 255, 0.65);
                  .ant-input-number-handler-up-inner, .ant-input-number-handler-down-inner {
                    color: rgba(255, 255, 255, 0.65);
                  }
                }
                .ant-input-number-handler:active {
                  background: transparent;
                }
              }
              .ant-radio-group {
                .ant-radio-button-wrapper {
                  background: transparent;
                  color: rgba(255, 255, 255, 0.65);
                  height: 27px;
                  line-height: 25px;
                  span {
                    font-style: inherit;
                  }
                }
              }
              .fileupload-form-container {
                .ant-btn {
                  height: 28px;
                }
                .ant-upload-list-item {
                  .ant-upload-list-item-info {
                    background: transparent;
                    color: rgba(255, 255, 255, 0.85);
                    i {
                      color: rgba(255, 255, 255, 0.85);
                    }
                  }
                  .anticon-close {
                    color: rgba(255, 255, 255, 0.85);
                  }
                  .anticon-close:hover {
                    color: rgba(255, 255, 255, 0.85);
                  }
                }
                .ant-upload-list-item:hover .ant-upload-list-item-info {
                  background: transparent;
                }
              }
            }
          }
        }
      }
    }
  }
}
.margin-popover {
  padding-top: 0px;
  .ant-popover-inner-content {
    width: 90px;
    padding: 0px 5px;
    .ant-menu-root.ant-menu-vertical {
      border: 0;
      .ant-menu-item {
        height: 30px;
        cursor: pointer;
        line-height: 30px;
      }
      .ant-menu-item:not(:last-child) {
        margin-bottom: 0px;
      }
      .ant-menu-item:first-child {
        margin-top: 10px;
      }
      .ant-menu-item:last-child {
        margin-bottom: 10px;
      }
    }
  }
  .ant-popover-arrow {
    display: none;
  }
}
src/mob/colorsketch/index.jsx
@@ -16,28 +16,30 @@
  }
  handleChange = (color) => {
    this.setState({ color: color.rgb })
    this.props.onChange(`rgba(${ color.rgb.r }, ${ color.rgb.g }, ${ color.rgb.b }, ${ color.rgb.a })`)
    let _color = `rgba(${ color.rgb.r }, ${ color.rgb.g }, ${ color.rgb.b }, ${ color.rgb.a })`
    this.setState({ color: _color }, () => {
      this.props.onChange(_color)
    })
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (nextProps.value !== this.state.color) {
      this.setState({ color: nextProps.value })
    }
  }
  render() {
    const { color } = this.state
    let _color = ''
    if (typeof(color) === 'string') {
      _color = color
    } else {
      _color = `rgba(${ color.r }, ${ color.g }, ${ color.b }, ${ color.a })`
    }
    return (
      <div className="color-sketch-block">
        <Popover content={
          <SketchPicker color={ this.state.color } onChange={ this.handleChange } />
          <SketchPicker color={ color } onChange={ this.handleChange } />
        } overlayClassName="color-sketch-popover" placement="bottomRight" title="" trigger="click">
          <div className="color-sketch-block-inner" style={ {background: _color} }></div>
          <div className="color-sketch-block-inner" style={ {background: color} }></div>
        </Popover>
        <div className="color-sketch-value">{_color}</div>
        <div className="color-sketch-value">{color}</div>
      </div>
    )
  }
src/templates/zshare/verifycard/customform/index.jsx
@@ -35,6 +35,16 @@
    })
  }
  handleCancel = () => {
    this.setState({
      editItem: null
    })
    this.props.form.setFieldsValue({
      sql: '',
      errmsg: ''
    })
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
@@ -160,6 +170,9 @@
            <Button onClick={this.handleConfirm} loading={this.state.loading} className="mk-green">
              保存
            </Button>
            <Button onClick={this.handleCancel}>
              取消
            </Button>
            {btn.sql ? <div style={{marginTop: '31px'}}>
              表名:  <div style={{wordBreak: 'break-all'}}>{btn.sql}</div>
            </div> : null}
src/templates/zshare/verifycard/index.jsx
@@ -1166,7 +1166,7 @@
  }
  uniqueChange = (values) => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    let verify = fromJS(this.state.verify).toJS()
    if (values.uuid) {
      verify.uniques = verify.uniques.map(item => {
@@ -1187,7 +1187,7 @@
  }
  contrastChange = (values) => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    let verify = fromJS(this.state.verify).toJS()
    if (values.uuid) {
      verify.contrasts = verify.contrasts.map(item => {
@@ -1208,7 +1208,7 @@
  }
  customChange = (values) => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    let verify = fromJS(this.state.verify).toJS()
    if (values.uuid) {
      verify.customverifys = verify.customverifys.map(item => {
@@ -1229,7 +1229,7 @@
  }
  scriptsChange = (values) => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    let verify = fromJS(this.state.verify).toJS()
    if (values.uuid) {
      verify.scripts = verify.scripts.map(item => {
@@ -1250,7 +1250,7 @@
  }
  orderChange = (values) => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    let verify = fromJS(this.state.verify).toJS()
    if (values.uuid) {
      verify.billcodes = verify.billcodes.map(item => {
@@ -1327,7 +1327,7 @@
  }
  handleStatus = (record, type) => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    let verify = fromJS(this.state.verify).toJS()
    record.status = record.status === 'false' ? 'true' : 'false'
    if (type === 'customverify') {
@@ -1378,7 +1378,7 @@
  }
  handleUpDown = (record, type, direction) => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    let verify = fromJS(this.state.verify).toJS()
    let index = 0
    if (type === 'customverify') {
@@ -1526,11 +1526,12 @@
  }
  handleConfirm = () => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    const { card } = this.props
    let verify = fromJS(this.state.verify).toJS()
    
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      if (verify.default === 'false' && verify.scripts.length === 0) {
      if (card.sqlType !== 'custom' && verify.default === 'false' && verify.scripts.length === 0) {
        notification.warning({
          top: 92,
          message: '不执行默认sql时,必须设置自定义脚本!',
src/templates/zshare/verifycard/index.scss
@@ -53,6 +53,10 @@
    }
    .add {
      padding-top: 4px;
      .mk-green {
        margin-bottom: 15px;
      }
    }
    .anticon-question-circle {
      color: #c49f47;
src/views/menudesign/index.jsx
@@ -27,7 +27,7 @@
const SourceWrap = asyncComponent(() => import('@/menu/modelsource'))
const MenuShell = asyncComponent(() => import('@/menu/menushell'))
const Controller = asyncComponent(() => import('@/menu/bgcontroller'))
// const DataSource = asyncComponent(() => import('@/mob/datasource'))
const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
sessionStorage.setItem('isEditState', 'true')
@@ -301,11 +301,11 @@
    config.components.forEach(item => {
      if (!error && item.setting) {
        if (item.setting.interType === 'system' && item.setting.execute !== 'false' && !item.setting.dataresource) {
          error = `组件《${item.setting.name}》未设置数据源`
          error = `组件《${item.name}》未设置数据源`
        } else if (item.setting.interType === 'system' && item.setting.execute === 'false' && item.scripts.length === 0) {
          error = `组件《${item.setting.name}》未设置数据源`
          error = `组件《${item.name}》未设置数据源`
        } else if (item.setting.interType && !item.setting.primaryKey) {
          error = `组件《${item.setting.name}》未设置主键`
          error = `组件《${item.name}》未设置主键`
        }
      }
    })
@@ -399,6 +399,7 @@
              </div>
            </div>
          </DndProvider>
          <StyleController />
        </div>
      </ConfigProvider>
    )