king
2021-03-17 4a23b7a8f9c23d7903019e48869c8c3191fa0ffc
2021-03-17
10个文件已修改
15个文件已添加
1597 ■■■■■ 已修改文件
src/assets/mobimg/form.png 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/cardcomponent/index.jsx 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/cardcomponent/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/cardcomponent/settingform/index.jsx 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/cardcomponent/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/dragelement/card.jsx 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/dragelement/index.jsx 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/dragelement/index.scss 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/dragelement/source.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/normal-form/index.jsx 301 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/normal-form/index.scss 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/wrapsetting/index.jsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/wrapsetting/settingform/index.jsx 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/wrapsetting/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/card.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modalconfig/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modalconfig/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modulesource/option.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/index.jsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/fieldscomponent/index.jsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/editcomponent/index.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/form.png
src/menu/components/form/cardcomponent/index.jsx
New file
@@ -0,0 +1,229 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal, Popover, Icon } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import SettingForm from './settingform'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const CardCellComponent = asyncComponent(() => import('@/menu/components/card/cardcellcomponent'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
class CardBoxComponent extends Component {
  static propTpyes = {
    cards: PropTypes.object,         // 卡片行配置信息
    card: PropTypes.object,          // 卡片配置信息
    move: PropTypes.func,            // 卡片移动
    deleteElement: PropTypes.func,   // 卡片删除
    updateElement: PropTypes.func    // 菜单配置更新
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,            // 卡片信息,包括正反面
    formlist: null,        // 设置表单信息
    elements: null,        // 编辑组
    visible: false,        // 模态框控制
    settingVisible: false,
  }
  /**
   * @description 搜索条件初始化
   */
  UNSAFE_componentWillMount () {
    const { card } = this.props
    this.setState({
      card: fromJS(card).toJS(),
      elements: fromJS(card.elements).toJS(),
    })
  }
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
  }
  shouldComponentUpdate (nextProps, nextState) {
    const { cards } = this.props
    return !is(fromJS(cards), fromJS(nextProps.cards)) || !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
  }
  getStyle = (comIds, style) => {
    const { cards } = this.props
    const { card } = this.state
    if (comIds.length !== 2 || comIds[0] !== cards.uuid || comIds[1] !== card.uuid) return
    let _card = fromJS(card).toJS()
    _card.style = style
    this.setState({
      card: _card
    })
    this.props.updateElement(_card)
  }
  updateCard = (elements) => {
    const { card } = this.state
    let _card = {...card, elements: elements}
    this.setState({
      card: _card
    })
    this.props.updateElement(_card)
  }
  addElement = () => {
    const { cards } = this.props
    const { card } = this.state
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.eleType = 'text'
    newcard.datatype = 'dynamic'
    newcard.height = 1
    // 注册事件-添加元素
    MKEmitter.emit('cardAddElement', [cards.uuid, card.uuid], newcard)
  }
  addButton = () => {
    const { cards } = this.props
    const { card } = this.state
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.eleType = 'button'
    newcard.label = 'button'
    newcard.sqlType = ''
    newcard.Ot = 'requiredSgl'
    newcard.OpenType = 'prompt'
    newcard.icon = ''
    newcard.class = 'primary'
    newcard.intertype = 'system'
    newcard.execSuccess = 'grid'
    newcard.execError = 'never'
    newcard.popClose = 'never'
    newcard.errorTime = 10
    newcard.verify = null
    newcard.show = 'link'
    // 注册事件-添加元素
    MKEmitter.emit('cardAddElement', [cards.uuid, card.uuid], newcard)
  }
  changeStyle = () => {
    const { cards } = this.props
    const { card } = this.state
    let options = ['background', 'border', 'padding', 'margin', 'shadow']
    MKEmitter.emit('changeStyle', [cards.uuid, card.uuid], options, fromJS(card.style).toJS())
  }
  settingSubmit = () => {
    const { card } = this.state
    this.settingRef.handleConfirm().then(res => {
      this.setState({
        settingVisible: false,
        card: {...card, setting: res}
      })
      this.props.updateElement({...card, setting: res})
    })
  }
  clickComponent = (e) => {
    if ((sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'propcard') && this.props.cards.subtype === 'propcard') {
      e.stopPropagation()
      MKEmitter.emit('clickComponent', this.state.card, this.props.cards, 'propcard')
    }
  }
  render() {
    const { cards } = this.props
    const { card, elements, settingVisible, dict } = this.state
    let _style = {...card.style}
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    _style.height = cards.style.height
    return (
      <div className="card-item" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <CardCellComponent cards={cards} cardCell={card} side="front" 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" title="编辑" type="edit" onClick={() => this.setState({settingVisible: true})} />
              <CopyComponent type="cardcell" card={card}/>
              <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
              {cards.subtype === 'propcard' ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                <div className="mk-popover-control">
                  <Icon className="plus" title="左移" type="arrow-left" onClick={() => this.props.move(card, 'left')} />
                  <Icon className="close" title="右移" type="arrow-right" onClick={() => this.props.move(card, 'right')} />
                </div>
              } trigger="hover" getPopupContainer={() => document.getElementById(card.uuid + 'swap')}>
                <Icon type="swap" id={card.uuid + 'swap'}/>
              </Popover> : null}
              {cards.subtype === 'propcard' ? <Icon className="close" title="删除卡片" type="delete" onClick={() => this.props.deleteElement(card)} /> : null}
            </div>
          } trigger="hover">
            <Icon type="tool" />
          </Popover>
        </div>
        <Modal
          wrapClassName="popview-modal"
          title={'卡片设置'}
          visible={settingVisible}
          width={700}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.settingSubmit}
          onCancel={() => { this.setState({ settingVisible: false }) }}
          destroyOnClose
        >
          <SettingForm
            dict={dict}
            cards={cards}
            setting={card.setting}
            inputSubmit={this.settingSubmit}
            wrappedComponentRef={(inst) => this.settingRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default CardBoxComponent
src/menu/components/form/cardcomponent/index.scss
src/menu/components/form/cardcomponent/settingform/index.jsx
New file
@@ -0,0 +1,194 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Radio, Tooltip, Icon, Input, Cascader, Select } from 'antd'
import './index.scss'
const { TextArea } = Input
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    cards: PropTypes.object,     // 卡片集
    setting: PropTypes.object,   // 数据源配置
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {
    type: this.props.setting.type || 'simple',
    click: this.props.setting.click || '',
    isApp: sessionStorage.getItem('appType') === 'pc',
    menulist: []
  }
  UNSAFE_componentWillMount() {
    const { isApp } = this.state
    let menulist = null
    if (isApp) {
      menulist = sessionStorage.getItem('appMenus')
    } else {
      menulist = sessionStorage.getItem('fstMenuList')
    }
    if (menulist) {
      try {
        menulist = JSON.parse(menulist)
      } catch {
        menulist = []
      }
    } else {
      menulist = []
    }
    this.setState({menulist})
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  handleSubmit = (e) => {
    e.preventDefault()
    if (this.props.inputSubmit) {
      this.props.inputSubmit()
    }
  }
  render() {
    const { setting, cards } = this.props
    const { getFieldDecorator } = this.props.form
    const { click, menulist, isApp } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <div className="model-menu-setting-form">
        <Form {...formItemLayout}>
          <Row gutter={24}>
            {cards.subtype === 'propcard' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="卡片点击时,向其他组件传递的BID值。">
                  <Icon type="question-circle" />
                  主键值
                </Tooltip>
              }>
                {getFieldDecorator('primaryId', {
                  initialValue: setting.primaryId || ''
                })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit}/>)}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="点击事件">
                {getFieldDecorator('click', {
                  initialValue: click
                })(
                  <Radio.Group onChange={(e) => this.setState({click: e.target.value})}>
                    <Radio value="">无</Radio>
                    <Radio value="menu">菜单</Radio>
                    <Radio value="link">链接</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {!isApp && click === 'menu' ? <Col span={12}>
              <Form.Item label="菜单">
                {getFieldDecorator('menu', {
                  initialValue: setting.menu || [],
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + '菜单!'
                    }
                  ]
                })(
                  <Cascader options={menulist} placeholder=""/>
                )}
              </Form.Item>
            </Col> : null}
            {isApp && click === 'menu' ? <Col span={12}>
              <Form.Item label="关联菜单">
                {getFieldDecorator('menu', {
                  initialValue: setting.menu || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + '关联菜单!'
                    }
                  ]
                })(
                  <Select
                    showSearch
                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  >
                    {menulist.map(option =>
                      <Select.Option key={option.MenuID} value={option.MenuID}>{option.MenuName}</Select.Option>
                    )}
                  </Select>
                )}
              </Form.Item>
            </Col> : null}
            {click === 'link' ? <Col span={24} className="textarea">
              <Form.Item label="链接">
                {getFieldDecorator('linkurl', {
                  initialValue: setting.linkurl || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '链接!'
                    }
                  ]
                })( <TextArea rows={2}/> )}
              </Form.Item>
            </Col> : null}
            {isApp ? <Col span={12}>
              <Form.Item label="打开方式">
                {getFieldDecorator('open', {
                  initialValue: setting.open || 'blank'
                })(
                  <Radio.Group>
                    <Radio value="blank">新窗口</Radio>
                    <Radio value="self">当前窗口</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            {click !== '' ? <Col span={12}>
              <Form.Item label="参数拼接">
                {getFieldDecorator('joint', {
                  initialValue: setting.joint || 'true'
                })(
                  <Radio.Group>
                    <Radio value="true">是</Radio>
                    <Radio value="false">否</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
          </Row>
        </Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/menu/components/form/cardcomponent/settingform/index.scss
New file
@@ -0,0 +1,11 @@
.model-menu-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
}
src/menu/components/form/dragelement/card.jsx
New file
@@ -0,0 +1,167 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Icon, Select, DatePicker, Input, InputNumber, Button, Popover, Switch, Radio, Checkbox } from 'antd'
import moment from 'moment'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const { MonthPicker } = DatePicker
const { TextArea } = Input
const Editor = asyncComponent(() => import('@/components/editor'))
const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
const CheckCard = asyncComponent(() => import('@/templates/modalconfig/checkCard'))
const Card = ({ id, card, cols, moveCard, findCard, editCard, closeCard, copyCard, showField }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'form', id, originalIndex },
    collect: monitor => ({
      isDragging: monitor.isDragging(),
    }),
  })
  const [, drop] = useDrop({
    accept: 'form',
    canDrop: () => true,
    drop: (item) => {
      const { id: draggedId, originalIndex } = item
      if (originalIndex === undefined) {
        item.dropTargetId = id
      } else if (draggedId && draggedId !== id) {
        const { index: overIndex } = findCard(id)
        moveCard(draggedId, overIndex)
      }
    }
  })
  const opacity = isDragging ? 0 : 1
  const edit = () => {
    editCard(id)
  }
  const close = () => {
    closeCard(id)
  }
  const copy = () => {
    copyCard(id)
  }
  let selectval = ''
  if (card.type === 'multiselect' || card.type === 'select' || card.type === 'link') {
    if (card.initval) {
      let _option = card.options.filter(option => option.Value === card.initval)[0]
      if (_option) {
        selectval = _option.Text || ''
      } else {
        selectval = ''
      }
    } else if (card.setAll === 'true') {
      selectval = card.emptyText || '空'
    }
  }
  let labelCol = 'ant-col-sm-8'
  let wrapCol = 'ant-col-sm-16'
  let isEntireLine = false
  if (card.entireLine === 'true' || ['textarea', 'hint', 'checkcard', 'brafteditor'].includes(card.type)) {
    isEntireLine = true
  }
  if (isEntireLine) {
    if (cols === '2') {
      labelCol = 'ant-col-sm-4'
      wrapCol = 'ant-col-sm-20'
    } else if (cols === '3') {
      labelCol = 'ant-col-cuslabel'
      wrapCol = 'ant-col-cuswrap'
    } else if (cols === '4') {
      labelCol = 'ant-col-sm-2'
      wrapCol = 'ant-col-sm-22'
    }
    if (card.hidelabel === 'true') {
      wrapCol = 'ant-col-sm-24'
    }
  }
  let formItem = null
  if (card.type === 'text') {
    formItem = (<Input style={{marginTop: '4px'}} value={card.initval} />)
  } else if (card.type === 'number') {
    formItem = (<InputNumber value={card.initval} precision={card.decimal} />)
  } else if (card.type === 'multiselect' || card.type === 'select' || card.type === 'link') {
    formItem = (<Select value={selectval}></Select>)
  } else if (card.type === 'color') {
    formItem = (<ColorSketch value={card.initval || 'transparent'}/>)
  } else if (card.type === 'date') {
    formItem = (<DatePicker value={card.initval ? moment().subtract(card.initval, 'days') : null} />)
  } else if (card.type === 'datemonth') {
    formItem = (<MonthPicker value={card.initval ? moment().subtract(card.initval, 'month') : null} />)
  } else if (card.type === 'datetime') {
    formItem = (<DatePicker showTime value={card.initval ? moment().subtract(card.initval, 'days') : null} />)
  } else if (card.type === 'textarea') {
    formItem = (<TextArea value={card.initval} autoSize={{ minRows: 2, maxRows: 6 }} />)
  } else if (card.type === 'brafteditor') {
    formItem = (<Editor />)
  } else if (card.type === 'fileupload') {
    formItem = (<Button style={{marginTop: '3px'}}><Icon type="upload" /> 点击上传 </Button>)
  } else if (card.type === 'funcvar') {
    formItem = (<Input style={{marginTop: '4px'}} value={card.linkfield} />)
  } else if (card.type === 'linkMain') {
    formItem = (<Input style={{marginTop: '4px'}} />)
  } else if (card.type === 'switch') {
    formItem = (<Switch checkedChildren={card.openText || ''} unCheckedChildren={card.closeText || ''} style={{marginTop: '8px'}} checked={card.initval}/>)
  } else if (card.type === 'radio') {
    formItem = card.options && card.options.length > 0 ? (<Radio.Group value={card.initval}>
      {card.options.map(cell => <Radio key={cell.key} value={cell.Value}>{cell.Text}</Radio>)}
    </Radio.Group>) : (<Radio.Group value={1}>
      <Radio value={1}>A</Radio>
      <Radio value={2}>B</Radio>
      <Radio value={3}>C</Radio>
      <Radio value={4}>D</Radio>
    </Radio.Group>)
  } else if (card.type === 'checkbox') {
    let _val = card.initval ? card.initval.split(',') : []
    formItem = card.options && card.options.length > 0 ? (<Checkbox.Group value={_val}>
      {card.options.map(cell => <Checkbox key={cell.key} value={cell.Value}>{cell.Text}</Checkbox>)}
    </Checkbox.Group>) : (<Checkbox.Group value={['A', 'C']}>
      <Checkbox value="A">A</Checkbox>
      <Checkbox value="B">B</Checkbox>
      <Checkbox value="C">C</Checkbox>
      <Checkbox value="D">D</Checkbox>
    </Checkbox.Group>)
  } else if (card.type === 'hint') {
    formItem = <div style={{marginTop: '10px', color: 'rgba(0, 0, 0, 0.85)'}}>{card.message}</div>
  } else if (card.type === 'checkcard') {
    formItem = <CheckCard width={card.width} ratio={card.ratio} display={card.display} fields={card.fields} options={card.options} />
  }
  return (
    <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
      <div className="mk-popover-control">
        <Icon className="edit" type="edit" onClick={edit} />
        <Icon className="copy" type="copy" onClick={copy} />
        <Icon className="close" type="close" onClick={close} />
      </div>
    } trigger="hover">
      <div className="page-card" style={{ opacity: opacity}}>
        <div ref={node => drag(drop(node))}>
          {<div className="ant-row ant-form-item">
            {card.hidelabel !== 'true' ? <div className={'ant-col ant-form-item-label ant-col-xs-24 ' + labelCol}>
              {card.label ? <label className={card.required === 'true' ? 'required' : ''}>{card.tooltip ?
                <Icon type="question-circle" /> : null}
                {card.label}</label> : null}
            </div> : null}
            <div className={'ant-col ant-form-item-control-wrapper ant-col-xs-24 ' + wrapCol}>
              {formItem}
              {showField ? card.field : ''}
            </div>
          </div>}
        </div>
      </div>
    </Popover>
  )
}
export default Card
src/menu/components/form/dragelement/index.jsx
New file
@@ -0,0 +1,157 @@
import React, { useState } from 'react'
import { useDrop } from 'react-dnd'
import { is, fromJS } from 'immutable'
import update from 'immutability-helper'
import { Col } from 'antd'
import Utils from '@/utils/utils.js'
import Card from './card'
import './index.scss'
const Container = ({list, group, setting, placeholder, handleList, handleForm, closeForm, showField }) => {
  const [cards, setCards] = useState(list)
  const moveCard = (id, atIndex) => {
    const { card, index } = findCard(id)
    if (!card) return
    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
    if (!group) {
      handleList(_cards)
    } else {
      handleList(_cards, group)
    }
  }
  if (!is(fromJS(cards), fromJS(list))) {
    setCards(list)
  }
  const findCard = id => {
    const card = cards.filter(c => `${c.uuid}` === id)[0]
    return {
      card,
      index: cards.indexOf(card),
    }
  }
  const editCard = id => {
    const { card } = findCard(id)
    handleForm(card)
  }
  const closeCard = id => {
    const { card } = findCard(id)
    closeForm(card)
  }
  const copyCard = id => {
    const { card, index: overIndex } = findCard(id)
    let _card = fromJS(card).toJS()
    _card.uuid = Utils.getuuid()
    _card.focus = true
    // 复制到剪切板
    let oInput = document.createElement('input')
    let val = JSON.parse(JSON.stringify(_card))
    val.copyType = 'form'
    oInput.value = window.btoa(window.encodeURIComponent(JSON.stringify(val)))
    document.body.appendChild(oInput)
    oInput.select()
    document.execCommand('Copy')
    oInput.className = 'oInput'
    oInput.style.display = 'none'
    document.body.removeChild(oInput)
    const _cards = update(cards, { $splice: [[overIndex + 1, 0, _card]] })
    setCards(_cards)
    if (!group) {
      handleList(_cards, null, null, _card)
    } else {
      handleList(_cards, group, null, _card)
    }
  }
  const [, drop] = useDrop({
    accept: 'form',
    drop(item) {
      if (item.hasOwnProperty('originalIndex') && group) {
        const { card } = findCard(item.id)
        if (!card) {
          handleList(cards, group, item.id)
        }
      }
      if (item.hasOwnProperty('originalIndex')) {
        return
      }
      let newcard = {}
      newcard.uuid = Utils.getuuid()
      newcard.label = 'label'
      newcard.type = item.subType
      newcard.resourceType = '0'
      newcard.options = []
      newcard.readonly = 'false'
      newcard.required = 'true'
      newcard.focus = true
      let targetId = ''
      if (item.dropTargetId) {
        targetId = item.dropTargetId
        delete item.dropTargetId
      } else if (cards.length > 0) {
        targetId = cards[cards.length - 1].uuid
      }
      const { index: overIndex } = findCard(`${targetId}`) // cards为空时 overIndex 为 -1
      const _cards = update(cards, { $splice: [[overIndex + 1, 0, newcard]] })
      setCards(_cards)
      if (!group) {
        handleList(_cards, null, null, newcard)
      } else {
        handleList(_cards, group, null, newcard)
      }
    }
  })
  let _cols = 24 / (setting.cols || 2)
  return (
    <div ref={drop} className="ant-row modal-fields-row">
      {cards.map(card => {
        let isEntireLine = false
        if (card.entireLine === 'true' || ['textarea', 'hint', 'checkcard', 'brafteditor'].includes(card.type)) {
          isEntireLine = true
        }
        return <Col key={card.uuid} className={isEntireLine ? 'textarea' + setting.cols : ''} span={isEntireLine ? 24 : _cols}>
          <Card
            id={card.uuid}
            cols={setting.cols}
            card={card}
            showField={showField}
            moveCard={moveCard}
            editCard={editCard}
            closeCard={closeCard}
            copyCard={copyCard}
            findCard={findCard}
          />
        </Col>
      })}
      {cards.length === 0 &&
        <div className="modal-drawarea-placeholder">
          {placeholder}
        </div>
      }
    </div>
  )
}
export default Container
src/menu/components/form/dragelement/index.scss
New file
@@ -0,0 +1,27 @@
.modal-source-item {
  display: block;
  box-shadow: 0px 0px 2px #bcbcbc;
  padding: 0.4rem 0.7rem;
  background-color: white;
  margin: 0px 0px 10px;
  cursor: move;
  border-radius: 4px;
}
.modal-drawarea-placeholder {
  width: 100%;
  line-height: 65px;
  text-align: center;
  color: #bcbcbc;
}
.modal-fields-row {
  padding-bottom: 35px;
  .ant-col {
    padding-left: 12px;
    padding-right: 12px;
  }
  >.ant-col {
    display: inline-block;
    float: none;
    vertical-align: top;
  }
}
src/menu/components/form/dragelement/source.jsx
New file
@@ -0,0 +1,13 @@
import React from 'react'
import { useDrag } from 'react-dnd'
import './index.scss'
const SourceElement = ({content}) => {
  const [, drag] = useDrag({ item: content })
  return (
    <div ref={drag} className="modal-source-item">
      {content.label}
    </div>
  )
}
export default SourceElement
src/menu/components/form/normal-form/index.jsx
New file
@@ -0,0 +1,301 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import { Icon, Popover, Modal, Carousel } 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('@/menu/components/form/wrapsetting'))
const CardComponent = asyncComponent(() => import('../dragelement'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
const { confirm } = Modal
class PropCardEditComponent extends Component {
  static propTpyes = {
    card: PropTypes.object,
    deletecomponent: PropTypes.func,
    updateConfig: PropTypes.func,
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    back: false
  }
  UNSAFE_componentWillMount () {
    const { card } = this.props
    if (card.isNew) {
      let _card = {
        uuid: card.uuid,
        type: card.type,
        floor: card.floor,
        tabId: card.tabId || '',
        parentId: card.parentId || '',
        format: 'object',   // 组件属性 - 数据格式
        pageable: false,    // 组件属性 - 是否可分页
        switchable: false,  // 组件属性 - 数据是否可切换
        dataName: card.dataName || '',
        width: card.width || 24,
        name: card.name,
        subtype: card.subtype,
        setting: { },
        wrap: { name: card.name, width: card.width || 24, datatype: 'static' },
        style: { marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px' },
        columns: [],
        scripts: [],
        subcards: [{
          uuid: Utils.getuuid(),
          setting: {},
          style: {},
          fields: [],
        }],
      }
      if (card.config) {
        let config = fromJS(card.config).toJS()
        _card.wrap = config.wrap
        _card.wrap.name = card.name
        _card.style = config.style
        _card.subcards = config.subcards.map(scard => {
          scard.uuid = Utils.getuuid()
          scard.fields = scard.fields.map(elem => {
            elem.uuid = Utils.getuuid()
            return elem
          })
          return scard
        })
      }
      this.setState({
        card: _card
      })
      this.props.updateConfig(_card)
    } else {
      this.setState({
        card: fromJS(card).toJS()
      })
    }
  }
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
    MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
    MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
  }
  updateComponentStyle = (parentId, keys, style) => {
    const { card } = this.state
    if (card.uuid !== parentId) return
    let subcards = card.subcards.map(item => {
      if (keys.includes(item.uuid)) {
        item.style = {...item.style, ...style}
      }
      return item
    })
    this.setState({card: {...card, subcards: []}}, () => {
      this.updateComponent({...card, subcards: subcards})
    })
  }
  /**
   * @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)
        let uuids = []
        cell.elements && cell.elements.forEach(c => {
          if (c.eleType === 'button') {
            uuids.push(c.uuid)
          }
        })
        MKEmitter.emit('delButtons', uuids)
        _this.setState({card})
        _this.props.updateConfig(card)
      },
      onCancel() {}
    })
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['height', '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: {},
      style: {},
      elements: [],
    }
    if (card.subcards.length > 0) {
      newcard = fromJS(card.subcards.slice(-1)[0]).toJS()
      newcard.uuid = Utils.getuuid()
      newcard.elements = newcard.elements.map(elem => {
        elem.uuid = Utils.getuuid()
        return elem
      })
    }
    card.subcards.push(newcard)
    this.setState({card})
    this.props.updateConfig(card)
  }
  move = (item, direction) => {
    let card = fromJS(this.state.card).toJS()
    let dragIndex = card.subcards.findIndex(c => c.uuid === item.uuid)
    let hoverIndex = null
    if (direction === 'left') {
      hoverIndex = dragIndex - 1
    } else {
      hoverIndex = dragIndex + 1
    }
    if (hoverIndex === -1 || hoverIndex === card.subcards.length) return
    card.subcards.splice(hoverIndex, 0, ...card.subcards.splice(dragIndex, 1))
    this.setState({card: {...card, subcards: []}}, () => {
      this.setState({card})
    })
    this.props.updateConfig(card)
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
      MKEmitter.emit('clickComponent', this.state.card)
    }
  }
  render() {
    const { card } = this.state
    return (
      <div className="menu-normal-form-edit-box" style={{...card.style}} onClick={this.clickComponent} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加分组" onClick={this.addCard} type="plus" />
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <CopyComponent type="propcard" card={card}/>
            <PasteComponent config={card} options={['cardcell']} updateConfig={this.updateComponent} />
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <UserComponent config={card}/>
            <Icon className="close" title="删除组件" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
            {card.wrap.datatype !== 'static' ? <SettingComponent config={card} updateConfig={this.updateComponent} /> : null}
            {card.wrap.datatype === 'static' ? <Icon style={{color: '#eeeeee', cursor: 'not-allowed'}} type="setting"/> : null}
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        {card.subcards.length > 0 ? <Carousel dotPosition={card.wrap.dotPosition || 'bottom'} effect={card.wrap.effect || 'scrollx'}>
          {card.subcards.map((subcard) => (<CardComponent key={subcard.uuid} cards={card} card={subcard} move={this.move} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
        </Carousel> : null}
      </div>
    )
  }
}
const mapStateToProps = (state) => {
  return {
    menu: state.customMenu
  }
}
const mapDispatchToProps = () => {
  return {}
}
export default connect(mapStateToProps, mapDispatchToProps)(PropCardEditComponent)
src/menu/components/form/normal-form/index.scss
New file
@@ -0,0 +1,92 @@
.menu-normal-form-edit-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 30px;
  .card-control {
    position: absolute;
    top: 0px;
    left: 0px;
    .anticon-tool {
      right: auto;
      left: 1px;
      padding: 1px;
    }
  }
  .anticon-tool {
    position: absolute;
    z-index: 2;
    font-size: 16px;
    right: 1px;
    top: 1px;
    cursor: pointer;
    padding: 5px;
    background: rgba(255, 255, 255, 0.55);
  }
  .card-item {
    overflow: hidden;
    position: relative;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
    min-height: 20px;
    height: 100%;
  }
  .ant-carousel:not(.ant-carousel-vertical) {
    .slick-dots li button {
      height: 8px;
    }
  }
  .ant-carousel.ant-carousel-vertical {
    .slick-dots li button {
      width: 8px;
    }
  }
  .ant-carousel {
    .slick-dots li button {
      background: #1890ff;
    }
  }
  .card-item:hover {
    box-shadow: 0px 0px 2px #1890ff;
  }
  .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-normal-form-edit-box::after {
  display: block;
  content: ' ';
  clear: both;
}
.menu-normal-form-edit-box:hover {
  z-index: 1;
  box-shadow: 0px 0px 4px #1890ff;
}
src/menu/components/form/wrapsetting/index.jsx
New file
@@ -0,0 +1,83 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Modal } from 'antd'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import SettingForm from './settingform'
import './index.scss'
class DataSource extends Component {
  static propTpyes = {
    config: PropTypes.any,
    updateConfig: PropTypes.func
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    visible: false,
    wrap: null
  }
  UNSAFE_componentWillMount () {
    const { config } = this.props
    this.setState({wrap: fromJS(config.wrap).toJS()})
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  editDataSource = () => {
    this.setState({
      visible: true
    })
  }
  verifySubmit = () => {
    const { config } = this.props
    this.verifyRef.handleConfirm().then(res => {
      this.setState({
        wrap: res,
        visible: false
      })
      this.props.updateConfig({...config, wrap: res})
    })
  }
  render () {
    const { config } = this.props
    const { visible, dict, wrap } = this.state
    return (
      <div className="model-menu-setting-wrap">
        <Icon type="edit" title="编辑" onClick={() => this.editDataSource()} />
        <Modal
          wrapClassName="popview-modal"
          title={config.type === 'table' ? '表格设置' : '卡片设置'}
          visible={visible}
          width={800}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <SettingForm
            dict={dict}
            wrap={wrap}
            config={config}
            inputSubmit={this.verifySubmit}
            wrappedComponentRef={(inst) => this.verifyRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default DataSource
src/menu/components/form/wrapsetting/index.scss
New file
@@ -0,0 +1,7 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-edit {
    color: #1890ff;
  }
}
src/menu/components/form/wrapsetting/settingform/index.jsx
New file
@@ -0,0 +1,253 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select } from 'antd'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    config: PropTypes.object,    // 卡片行信息
    wrap: PropTypes.object,      // 数据源配置
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {
    roleList: []
  }
  UNSAFE_componentWillMount () {
    let roleList = sessionStorage.getItem('sysRoles')
    if (roleList) {
      try {
        roleList = JSON.parse(roleList)
      } catch {
        roleList = []
      }
    } else {
      roleList = []
    }
    this.setState({roleList})
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  handleSubmit = (e) => {
    e.preventDefault()
    if (this.props.inputSubmit) {
      this.props.inputSubmit()
    }
  }
  render() {
    const { wrap, config } = this.props
    const { getFieldDecorator } = this.props.form
    const { roleList } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <div className="model-menu-setting-form">
        <Form {...formItemLayout}>
          <Row gutter={24}>
            <Col span={12}>
              <Form.Item label="标题">
                {getFieldDecorator('title', {
                  initialValue: wrap.title || ''
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="用于组件间的区分。">
                  <Icon type="question-circle" />
                  组件名称
                </Tooltip>
              }>
                {getFieldDecorator('name', {
                  initialValue: wrap.name,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '组件名称!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="栅格布局,每行等分为24列。">
                  <Icon type="question-circle" />
                  宽度
                </Tooltip>
              }>
                {getFieldDecorator('width', {
                  initialValue: wrap.width || 24,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '宽度!'
                    }
                  ]
                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            {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" />
                  分页风格
                </Tooltip>
              }>
                {getFieldDecorator('pagestyle', {
                  initialValue: wrap.pagestyle || 'page'
                })(
                  <Radio.Group>
                    <Radio value="page">页码</Radio>
                    <Radio value="switch">左右切换</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            {config.subtype !== 'tablecard' ? <Col span={12}>
              <Form.Item label="卡片属性">
                {getFieldDecorator('cardType', {
                  initialValue: wrap.cardType || ''
                })(
                  <Radio.Group style={{whiteSpace: 'nowrap'}}>
                    <Radio key="" value=""> 不可选 </Radio>
                    <Radio key="radio" value={'radio'}> 单选 </Radio>
                    {config.subtype !== 'propcard' ? <Radio key="checkbox" value={'checkbox'}> 多选 </Radio> : null}
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            {config.subtype !== 'tablecard' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="设置为居中对齐或右对齐,只在卡片为1行时有效。">
                  <Icon type="question-circle" />
                  卡片排列
                </Tooltip>
              }>
                {getFieldDecorator('cardFloat', {
                  initialValue: wrap.cardFloat || 'left'
                })(
                  <Radio.Group style={{whiteSpace: 'nowrap'}}>
                    <Radio key="left" value="left"> 左对齐 </Radio>
                    <Radio key="center" value="center"> 居中 </Radio>
                    <Radio key="right" value="right"> 右对齐 </Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            {config.subtype !== 'tablecard' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="鼠标悬浮于卡片上方时,卡片放大1.05倍。">
                  <Icon type="question-circle" />
                  卡片放大
                </Tooltip>
              }>
                {getFieldDecorator('scale', {
                  initialValue: wrap.scale || 'false'
                })(
                  <Radio.Group>
                    <Radio key="false" value="false"> 否 </Radio>
                    <Radio key="true" value="true"> 是 </Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            {config.subtype === 'tablecard' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="表格高度,超出时滚动,高度为空时根据内容自适应。">
                  <Icon type="question-circle" />
                  高度
                </Tooltip>
              }>
                {getFieldDecorator('height', {
                  initialValue: wrap.height
                })(<InputNumber min={100} max={2000} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col> : null}
            {config.subtype === 'propcard' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="选择类型为《页眉/页脚》时,打印的每页里都会带有该组件。">
                  <Icon type="question-circle" />
                  组件类型
                </Tooltip>
              }>
                {getFieldDecorator('printType', {
                  initialValue: wrap.printType || 'content'
                })(
                  <Radio.Group>
                    <Radio value="content">内容</Radio>
                    <Radio value="headerOrfooter">页眉/页脚</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="黑名单">
                {getFieldDecorator('blacklist', {
                  initialValue: wrap.blacklist || []
                })(
                  <Select
                    showSearch
                    mode="multiple"
                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  >
                    {roleList.map(option =>
                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
                    )}
                  </Select>
                )}
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/menu/components/form/wrapsetting/settingform/index.scss
New file
@@ -0,0 +1,11 @@
.model-menu-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
}
src/menu/menushell/card.jsx
@@ -14,6 +14,7 @@
const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
const NormalForm = asyncComponent(() => import('@/menu/components/form/normal-form'))
const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
@@ -58,6 +59,8 @@
      return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'pie') {
      return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'form') {
      return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'tabs') {
      return (<AntvTabs tabs={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'card' && card.subtype === 'datacard') {
src/menu/menushell/index.jsx
@@ -92,6 +92,7 @@
        editor: '富文本',
        code: '自定义',
        carousel: '轮播',
        form: '表单',
        card: '卡片'
      }
      let i = 1
src/menu/modalconfig/index.jsx
@@ -259,7 +259,7 @@
      if (_config.groups.length > 0) {
        _config.groups.forEach(group => {
          group.sublist = group.sublist.map(item => {
            if (item.uuid !== res.uuid && item.field.toLowerCase() === res.field.toLowerCase()) {
            if (item.uuid !== res.uuid && res.field && item.field && item.field.toLowerCase() === res.field.toLowerCase()) {
              fieldrepet = true
            } else if (item.uuid !== res.uuid && item.label === res.label) {
              labelrepet = true
@@ -274,7 +274,7 @@
        })
      } else {
        _config.fields = _config.fields.map(item => {
          if (item.uuid !== res.uuid && item.field.toLowerCase() === res.field.toLowerCase()) {
          if (item.uuid !== res.uuid && res.field && item.field && item.field.toLowerCase() === res.field.toLowerCase()) {
            fieldrepet = true
          } else if (item.uuid !== res.uuid && item.label === res.label) {
            labelrepet = true
src/menu/modalconfig/index.scss
@@ -216,6 +216,10 @@
                width: 100%;
                margin-top: 4px;
              }
              .normal-braft-editor {
                border: 1px solid #d9d9d9;
                border-radius: 4px;
              }
            }
            .ant-form-item-control-wrapper::after {
              content: '';
src/menu/modulesource/option.jsx
@@ -16,6 +16,7 @@
import Mainsearch from '@/assets/mobimg/mainsearch.png'
import Carousel from '@/assets/mobimg/carousel.png'
import Carousel1 from '@/assets/mobimg/carousel1.png'
import form from '@/assets/mobimg/form.png'
// 组件配置信息
export const menuOptions = [
@@ -23,6 +24,7 @@
  { type: 'menu', url: Mainsearch, component: 'search', subtype: 'mainsearch', title: '搜索条件', width: 24, forbid: ['billPrint'] },
  { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '数据卡', width: 24 },
  { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '属性卡', width: 24 },
  { type: 'menu', url: form, component: 'form', subtype: 'form', title: '表单', width: 24 },
  { type: 'menu', url: Carousel, component: 'carousel', subtype: 'datacard', title: '轮播-动态数据', width: 24, forbid: ['billPrint'] },
  { type: 'menu', url: Carousel1, component: 'carousel', subtype: 'propcard', title: '轮播-静态数据', width: 24, forbid: ['billPrint'] },
  { type: 'menu', url: NormalTable, component: 'table', subtype: 'normaltable', title: '常用表', width: 24 },
src/tabviews/zshare/mutilform/index.jsx
@@ -1391,14 +1391,14 @@
  render() {
    const { cols } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
      // labelCol: {
      //   xs: { span: 24 },
      //   sm: { span: 8 }
      // },
      // wrapperCol: {
      //   xs: { span: 24 },
      //   sm: { span: 16 }
      // }
    }
    return (
src/templates/formtabconfig/index.jsx
@@ -905,7 +905,7 @@
        if (_ismutil && group.sublist.length === 0) {
          _config.enabled = false
        }
        let arr = group.sublist.filter(item => item.field.toLowerCase() === _primary)
        let arr = group.sublist.filter(item => item.field && item.field.toLowerCase() === _primary)
        if (arr.length > 0) {
          _config.enabled = false
@@ -1619,7 +1619,7 @@
    let primaryrepeat = false
    config.groups.forEach(group => {
      let arr = group.sublist.filter(item => item.field.toLowerCase() === _primary)
      let arr = group.sublist.filter(item => item.field && item.field.toLowerCase() === _primary)
      if (arr.length > 0) {
        primaryrepeat = true
src/templates/modalconfig/index.jsx
@@ -359,7 +359,7 @@
      if (_config.groups.length > 0) {
        _config.groups.forEach(group => {
          group.sublist = group.sublist.map(item => {
            if (item.uuid !== res.uuid && item.field.toLowerCase() === res.field.toLowerCase()) {
            if (item.uuid !== res.uuid && res.field && item.field && item.field.toLowerCase() === res.field.toLowerCase()) {
              fieldrepet = true
            } else if (item.uuid !== res.uuid && item.label === res.label) {
              labelrepet = true
@@ -374,7 +374,7 @@
        })
      } else {
        _config.fields = _config.fields.map(item => {
          if (item.uuid !== res.uuid && item.field.toLowerCase() === res.field.toLowerCase()) {
          if (item.uuid !== res.uuid && res.field && item.field && item.field.toLowerCase() === res.field.toLowerCase()) {
            fieldrepet = true
          } else if (item.uuid !== res.uuid && item.label === res.label) {
            labelrepet = true
src/templates/sharecomponent/fieldscomponent/index.jsx
@@ -70,7 +70,7 @@
      if (config.groups.length > 1) {
        config.groups.forEach(group => {
          group.sublist.forEach(item => {
            if (columns.has(item.field.toLowerCase())) {
            if (item.field && columns.has(item.field.toLowerCase())) {
              let _datatype = columns.get(item.field.toLowerCase()).datatype
              columns.set(item.field.toLowerCase(), {...item, selected: true, datatype: _datatype})
            }
@@ -78,7 +78,7 @@
        })
      } else {
        config.fields.forEach(item => {
          if (columns.has(item.field.toLowerCase())) {
          if (item.field && columns.has(item.field.toLowerCase())) {
            let _datatype = columns.get(item.field.toLowerCase()).datatype
            columns.set(item.field.toLowerCase(), {...item, selected: true, datatype: _datatype})
          }
@@ -243,7 +243,7 @@
        config.groups.forEach(group => {
          let _items = []
          group.sublist.forEach(item => {
            if (columnsMap.has(item.field.toLowerCase())) {
            if (item.field && columnsMap.has(item.field.toLowerCase())) {
              let cell = columnsMap.get(item.field.toLowerCase())
      
              if (cell.selected && cell.type === item.type) { // 数据选择状态及类型未修改时,直接添加
@@ -283,7 +283,7 @@
        config.groups[config.groups.length - 1].sublist = [...config.groups.slice(-1)[0].sublist, ..._additems]
      } else {
        config.fields.forEach(item => {
          if (columnsMap.has(item.field.toLowerCase())) {
          if (item.field && columnsMap.has(item.field.toLowerCase())) {
            let cell = columnsMap.get(item.field.toLowerCase())
    
            if (cell.selected && cell.type === item.type) { // 数据选择状态及类型未修改时,直接添加
src/templates/zshare/editcomponent/index.jsx
@@ -224,7 +224,7 @@
        if (_config.groups.length > 0) {
          _config.groups.forEach(group => {
            group.sublist.forEach(item => {
              fields.push(item.field.toLowerCase())
              item.field && fields.push(item.field.toLowerCase())
              labels.push(item.label)
            })
            if (group.default) {
@@ -233,13 +233,13 @@
          })
        } else {
          _config.fields.forEach(item => {
            fields.push(item.field.toLowerCase())
            item.field && fields.push(item.field.toLowerCase())
            labels.push(item.label)
          })
          _config.fields.push(res)
        }
        if (fields.includes(res.field.toLowerCase())) {
        if (res.field && fields.includes(res.field.toLowerCase())) {
          notification.warning({
            top: 92,
            message: '字段已存在!',