king
2021-07-06 71d4765ba3cdc4a004687cd37a529b565e9ea9fd
2021-07-06
23个文件已修改
10个文件已添加
1715 ■■■■ 已修改文件
src/menu/components/card/data-card/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/normal-form/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/actionform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modalconfig/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/formdragelement/card.jsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/formdragelement/index.scss 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/index.jsx 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/index.scss 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/pastecomponent/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/controller.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/groupdragelement/card.jsx 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/groupdragelement/index.jsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/groupdragelement/index.scss 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/groupform/index.jsx 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/groupform/index.scss 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/index.jsx 355 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/index.scss 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/pastecomponent/index.jsx 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/searchdragelement/card.jsx 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/searchdragelement/index.jsx 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/searchdragelement/index.scss 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/settingform/index.jsx 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/settingform/index.scss 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/source.jsx 93 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/index.jsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/searchform/index.jsx 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/editcomponent/index.jsx 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/formconfig.jsx 77 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/modalform/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.jsx
@@ -307,7 +307,7 @@
        }
      }
      MKEmitter.emit('changeModal', card, btn)
    } else if (btn.OpenType === 'popview') {
    } else if (btn.OpenType === 'popview' && appType !== 'mob') {
      MKEmitter.emit('changePopview', card, btn)
    }
  }
src/menu/components/form/normal-form/index.jsx
@@ -9,13 +9,13 @@
import asyncIconComponent from '@/utils/asyncIconComponent'
import { getModalForm } from '@/templates/zshare/formconfig'
import { resetStyle } from '@/utils/utils-custom.js'
import ModalForm from '@/templates/zshare/modalform'
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 ModalForm = asyncComponent(() => import('@/templates/zshare/modalform'))
const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
const WrapComponent = asyncIconComponent(() => import('@/menu/components/form/wrapsetting'))
const CardComponent = asyncComponent(() => import('@/templates/modalconfig/dragelement'))
src/menu/components/search/main-search/index.jsx
@@ -11,7 +11,6 @@
import { getSearchForm } from '@/templates/zshare/formconfig'
import { resetStyle } from '@/utils/utils-custom.js'
import asyncIconComponent from '@/utils/asyncIconComponent'
import SearchForm from '@/templates/sharecomponent/searchcomponent/searchform'
import DragElement from './dragsearch'
import MKEmitter from '@/utils/events.js'
import './index.scss'
@@ -19,6 +18,7 @@
const { confirm } = Modal
const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
const SearchForm = asyncIconComponent(() => import('@/templates/sharecomponent/searchcomponent/searchform'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -629,7 +629,7 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'icon') { // 多选
      } else if (item.type === 'icon') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
src/menu/modalconfig/index.jsx
@@ -12,7 +12,6 @@
import enUS from '@/locales/en-US/model.js'
import { getModalForm } from '@/templates/zshare/formconfig'
import ModalForm from '@/templates/zshare/modalform'
import SourceElement from '@/templates/modalconfig/dragelement/source'
import SettingForm from '@/templates/modalconfig/settingform'
import asyncComponent from '@/utils/asyncComponent'
@@ -22,6 +21,7 @@
const { Panel } = Collapse
const { confirm } = Modal
const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
const ModalForm = asyncComponent(() => import('@/templates/zshare/modalform'))
const EditComponent = asyncComponent(() => import('@/templates/zshare/editcomponent'))
const DragElement = asyncComponent(() => import('@/templates/modalconfig/dragelement'))
const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
src/mob/components/formdragelement/card.jsx
@@ -113,16 +113,18 @@
    }
    formItem = (
    <div className="am-list-item checkbox">
    <div className={'am-list-item checkbox mk-radio ' + (card.arrange || '')}>
      <div className="am-list-line">
        <div className="am-input-label">{card.label}</div>
        <div className="am-input-control">
          <div className="mk-radio-group">
          {card.arrange !== 'line' ? <Checkbox.Group value={[card.initval]}>
            {options.map(cell => <Checkbox key={cell.key} value={cell.Value}>{cell.Text}</Checkbox>)}
          </Checkbox.Group> : <div className="mk-radio-group">
            {options.map(cell => (<div key={cell.key} className="mk-radio-wrapper">
              <span className="radio-value">{cell.Text}</span>
              <span className={'radio-check ' + (card.initval === cell.Value ? 'checked' : '')}></span>
            </div>))}
          </div>
          </div>}
        </div>
      </div>
    </div>)
@@ -140,7 +142,7 @@
    }
    formItem = (
      <div className="am-list-item checkbox">
      <div className={'am-list-item checkbox ' + (card.arrange || '')}>
        <div className="am-list-line">
          <div className="am-input-label">{card.label}</div>
          <div className="am-input-control">
src/mob/components/formdragelement/index.scss
@@ -22,7 +22,7 @@
    align-items: center;
    .am-list-line {
      border-bottom: 1PX solid #ddd;
      border-bottom: 1px solid #ddd;
      align-items: center;
      position: relative;
      display: flex;
@@ -168,6 +168,28 @@
      }
    }
  }
  .am-list-item.checkbox.mk-radio {
    .ant-checkbox-inner {
      border-radius: 50%;
    }
  }
  .am-list-item.checkbox:not(.line) {
    .ant-checkbox-group {
      .ant-checkbox-wrapper {
        float: left;
        margin-right: 15px;
        .ant-checkbox + span {
          border-bottom: none;
        }
      }
    }
    .mk-radio-group {
      .mk-radio-wrapper {
        float: left;
        margin-right: 15px;
      }
    }
  }
  .split-line {
    color: #1890ff;
    font-size: 16px;
src/mob/components/topbar/normal-navbar/index.jsx
@@ -59,6 +59,7 @@
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
    MKEmitter.addListener('submitSearch', this.getSearch)
  }
  shouldComponentUpdate (nextProps, nextState) {
@@ -73,6 +74,7 @@
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
    MKEmitter.removeListener('submitSearch', this.getSearch)
  }
  
  /**
@@ -130,8 +132,31 @@
    }
  }
  setSearch = () => {
  getSearch = (config) => {
    const { card } = this.state
    if (card.uuid !== config.uuid) return
    this.setState({
      card: config
    })
    this.props.updateConfig(config)
  }
  setSearch = () => {
    let card = fromJS(this.state.card).toJS()
    if (!card.search) {
      card.search = {
        floor: 1,
        setting: { type: 'title', field: '', title: '', focus: 'true', btn: 'hidden' },
        groups: [],
        fields: []
      }
    }
    this.props.updateConfig(card)
    MKEmitter.emit('changeSearch', card)
  }
  render() {
@@ -141,7 +166,6 @@
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    _style.height = card.wrap.height
    return (
      <div className="normal-topbar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
src/mob/components/topbar/normal-navbar/index.scss
@@ -8,7 +8,7 @@
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 30px;
  height: 50px;
  z-index: 3;
  
  .anticon-tool {
@@ -31,6 +31,8 @@
      width: 30px;
      text-align: left;
      color: #1890ff;
      font-size: 20px;
      line-height: 50px;
    }
    .am-navbar-title {
      text-align: center;
@@ -42,23 +44,26 @@
      flex: 1;
      padding: 0 5px;
      .search-bar {
        height: 35px;
        line-height: 35px;
        height: 30px;
        line-height: 30px;
        padding: 0 10px;
        color:rgba(0, 0, 0, 0.65);
        border: 1px solid #e8e8e8;
        background: #efefef;
        border: 1px solid #E9EDF0;
        background: #E9EDF0;
        border-radius: 4px;
        position: relative;
        top: 50%;
        transform: translateY(-50%);
        cursor: pointer;
        font-size: 18px;
      }
    }
    .am-navbar-right {
      text-align: right;
      color: #1890ff;
      min-width: 30px;
      font-size: 20px;
      line-height: 50px;
      .anticon-search {
        margin-right: 5px;
        padding: 5px;
src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, InputNumber, Radio, Tooltip, Icon } from 'antd'
import { Form, Row, Col, Input, Radio, Tooltip, Icon } from 'antd'
import './index.scss'
@@ -74,7 +74,7 @@
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
            {/* <Col span={12}>
              <Form.Item label="高度">
                {getFieldDecorator('height', {
                  initialValue: wrap.height || 50,
@@ -86,7 +86,7 @@
                  ]
                })(<InputNumber min={30} max={200} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            </Col> */}
            <Col span={12}>
              <Form.Item label="返回">
                {getFieldDecorator('back', {
src/mob/modalconfig/index.jsx
@@ -12,7 +12,6 @@
import enUS from '@/locales/en-US/model.js'
import { getModalForm } from '@/templates/zshare/formconfig'
import ModalForm from '@/templates/zshare/modalform'
import SourceElement from '@/templates/modalconfig/dragelement/source'
import SettingForm from '@/templates/modalconfig/settingform'
import asyncComponent from '@/utils/asyncComponent'
@@ -23,6 +22,7 @@
const { confirm } = Modal
const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
const PasteComponent = asyncComponent(() => import('./pastecomponent'))
const ModalForm = asyncComponent(() => import('@/templates/zshare/modalform'))
const DragElement = asyncComponent(() => import('@/mob/components/formdragelement'))
const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
@@ -365,6 +365,14 @@
    })
  }
  insert = (config) => {
    this.setState({
      config
    }, () => {
      this.handleForm(config.fields[config.fields.length - 1])
    })
  }
  render () {
    const { config, dict } = this.state
@@ -391,7 +399,7 @@
            <Button icon="setting" onClick={this.changeSetting}>设置</Button>
            <Button type="primary" onClick={this.submitConfig}>保存</Button>
            <Button onClick={this.cancelConfig}>返回</Button>
            <PasteComponent type="menu" Tab={null} insert={this.insert} />
            <PasteComponent config={config} updateConfig={this.insert} />
            <Switch checkedChildren={dict['model.switch.open']} unCheckedChildren={dict['model.switch.close']} defaultChecked={this.state.showField} onChange={(val) => this.setState({showField: val})} />
          </div>
          <div className="setting">
src/mob/modalconfig/pastecomponent/index.jsx
@@ -34,13 +34,13 @@
      if (res.field && keys.includes(res.field.toLowerCase())) {
        notification.warning({
          top: 92,
          message: '搜索字段已存在!',
          message: '字段已存在!',
          duration: 5
        })
        return
      }
      this.props.updateConfig(config)
      this.props.updateConfig({...config, fields: [...config.fields, res]})
      this.setState({visible: false})
      notification.success({
src/mob/searchconfig/controller.jsx
@@ -6,7 +6,6 @@
class SearchController extends Component {
  state = {
    btn: null,
    config: null,
    visible: false
  }
@@ -29,34 +28,33 @@
    MKEmitter.removeListener('changeSearch', this.initConfig)
  }
  initConfig = (config, btn) => {
  initConfig = (config) => {
    this.setState({
      visible: true,
      config: fromJS(config).toJS(),
      btn: fromJS(btn).toJS()
      config: fromJS(config).toJS()
    })
  }
  handleBack = () => {
    this.setState({
      visible: false,
      config: null,
      btn: null
      config: null
    })
  }
  
  handleSave = (modal) => {
    const { config, btn } = this.state
    MKEmitter.emit('submitSearch', config, btn, modal)
  handleSave = (search) => {
    const { config } = this.state
    MKEmitter.emit('submitSearch', {...config, search})
  }
  render () {
    const { config, btn, visible } = this.state
    const { config, visible } = this.state
    if (!visible) return null
    return (
      <SearchConfig btn={btn} componentConfig={config} handleBack={this.handleBack} handleSave={this.handleSave}/>
      <SearchConfig config={config.search} handleBack={this.handleBack} handleSave={this.handleSave}/>
    )
  }
}
src/mob/searchconfig/groupdragelement/card.jsx
New file
@@ -0,0 +1,61 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Icon, Popover } from 'antd'
import './index.scss'
const Card = ({ id, card, moveCard, findCard, editCard, closeCard, changeGroup }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'search', id, originalIndex },
    collect: monitor => ({
      isDragging: monitor.isDragging(),
    }),
  })
  const [, drop] = useDrop({
    accept: 'search',
    canDrop: () => true,
    drop: (item) => {
      const { id: draggedId } = item
      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 change = () => {
    changeGroup(id)
  }
  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="close" type="close" onClick={close} />
      </div>
    } trigger="hover">
      <div className="page-card" onDoubleClick={change} style={{ opacity: opacity}}>
        <div ref={node => drag(drop(node))}>
          {card.wrap.icon ? <div className="icon">
            <Icon type={card.wrap.icon} />
          </div> : null}
          <div className="name">
            {card.wrap.name}
          </div>
        </div>
      </div>
    </Popover>
  )
}
export default Card
src/mob/searchconfig/groupdragelement/index.jsx
New file
@@ -0,0 +1,64 @@
import React, { useState } from 'react'
import { is, fromJS } from 'immutable'
import update from 'immutability-helper'
import Card from './card'
import './index.scss'
const Container = ({list, handleList, handleForm, closeForm, handleGroup }) => {
  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]] })
    handleList(_cards)
  }
  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 changeGroup = id => {
    const { card } = findCard(id)
    handleGroup(card)
  }
  return (
    <div className="ant-row modal-search-groups-row">
      {cards.map(card => {
        return <Card
          id={card.uuid}
          key={card.uuid}
          card={card}
          moveCard={moveCard}
          editCard={editCard}
          closeCard={closeCard}
          changeGroup={changeGroup}
          findCard={findCard}
        />
      })}
    </div>
  )
}
export default Container
src/mob/searchconfig/groupdragelement/index.scss
New file
@@ -0,0 +1,27 @@
.modal-search-groups-row {
  min-height: 20px!important;
  padding-right: 30px;
  margin-bottom: 15px;
  .page-card {
    float: left;
    width: 13.333%;
    text-align: center;
    padding: 5px;
    .icon {
      background: #f0f0f0;
      color: #000000;
      border-radius: 30px;
      width: 35px;
      height: 35px;
      line-height: 35px;
      font-size: 18px;
      margin: 0 auto 5px;
    }
    .name {
      font-size: 13px;
      white-space: nowrap;
      color: rgba(0, 0, 0, 0.85);
    }
  }
}
src/mob/searchconfig/groupform/index.jsx
New file
@@ -0,0 +1,90 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input } from 'antd'
import { formRule } from '@/utils/option.js'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const MkIcon = asyncComponent(() => import('@/components/mkIcon'))
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,     // 字典项
    config: PropTypes.object,   // 表单配置信息
    inputSubmit: PropTypes.any  // 回车提交事件
  }
  state = {}
  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 { config } = this.props
    const { getFieldDecorator } = this.props.form
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout} className="ant-advanced-search-form modal-setting-form">
        <Row gutter={24}>
          <Col span={22}>
            <Form.Item label="名称">
              {getFieldDecorator('name', {
                initialValue: config.wrap.name,
                rules: [
                  {
                    required: true,
                    message: '请填写名称!'
                  },
                  {
                    max: formRule.input.max,
                    message: formRule.input.message
                  }
                ]
              })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />)}
            </Form.Item>
          </Col>
          <Col span={22}>
            <Form.Item label="图标">
              {getFieldDecorator('icon', {
                initialValue: config.wrap.icon
              })(
                <MkIcon allowClear/>
              )}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
  }
}
export default Form.create()(SettingForm)
src/mob/searchconfig/groupform/index.scss
New file
@@ -0,0 +1,18 @@
.ant-advanced-search-form.modal-setting-form {
  .textarea {
    .ant-form-item-label {
      width: 16.3%;
    }
    .ant-form-item-control-wrapper {
      width: 83.33333333%;
    }
  }
  .ant-input-number {
    width: 100%;
  }
  .anticon-question-circle {
    color: #c49f47;
    position: relative;
    left: -3px;
  }
}
src/mob/searchconfig/index.jsx
@@ -10,21 +10,20 @@
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import { getModalForm } from '@/templates/zshare/formconfig'
import ModalForm from '@/templates/zshare/modalform'
import { getSearchForm } from '@/templates/zshare/formconfig'
import SourceElement from '@/templates/modalconfig/dragelement/source'
import SettingForm from '@/templates/modalconfig/settingform'
import SettingForm from './settingform'
import asyncComponent from '@/utils/asyncComponent'
import { SearchItems } from './source'
import './index.scss'
const { Panel } = Collapse
const { confirm } = Modal
const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
const SearchForm = asyncComponent(() => import('@/templates/sharecomponent/searchcomponent/searchform'))
const PasteComponent = asyncComponent(() => import('./pastecomponent'))
const DragElement = asyncComponent(() => import('@/mob/components/formdragelement'))
const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
const DragElement = asyncComponent(() => import('./searchdragelement'))
const GDragElement = asyncComponent(() => import('./groupdragelement'))
const GroupForm = asyncComponent(() => import('./groupform'))
class ComModalConfig extends Component {
  static propTpyes = {
@@ -34,7 +33,7 @@
  }
  state = {
    dict: CommonDict,      // 字典
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,      // 字典
    config: null,          // 页面配置,包括模板类型、模态框设置、添加表名、表单列表
    visible: false,        // 表单编辑模态框,显示控制
    formlist: null,        // 表单编辑模态框,可编辑字段
@@ -43,21 +42,20 @@
    originConfig: null,    // 原始菜单
    sqlVerifing: false,    // sql验证
    showField: false,      // 显示表单字段值
    standardform: null
    group: null,
    editGroup: null
  }
  /**
   * @description 数据预处理
   */
  UNSAFE_componentWillMount () {
    const { btn } = this.props
    let _config = btn.modal
    _config.version = '1.0'
    const { config } = this.props
    this.setState({
      config: _config,
      originConfig: fromJS(_config).toJS()
      group: fromJS(config).toJS(),
      config: fromJS(config).toJS(),
      originConfig: fromJS(config).toJS()
    })
  }
@@ -72,105 +70,74 @@
  /**
   * @description 表单变化
   * 1、表单拖拽添加时,检查是否存在示例表单,如存在则去除示例
   * 2、表单移动后,保存移动后的顺序
   * 3、新增表单时,直接打开编辑框
   */
  handleList = (list, newcard) => {
    let _config = fromJS(this.state.config).toJS()
    let _group = {...this.state.group, fields: list}
    if (list.length > _config.fields.length) {
      _config.fields = list.filter(item => !item.origin)
      this.setState({
        config: _config
      }, () => {
    this.setState({
      group: _group
    }, () => {
      if (newcard) {
        this.handleForm(newcard)
      })
      }
    })
    this.resetConfig(_group)
  }
  /**
   * @description 分组变化
   */
  handleGroupList = (list) => {
    let _group = {...this.state.group, groups: list}
    this.setState({
      group: _group
    })
    this.resetConfig(_group)
  }
  /**
   * @description 表单变化
   */
  handleList = (list, newcard) => {
    let _group = {...this.state.group, fields: list}
    this.setState({
      group: _group
    }, () => {
      if (newcard) {
        this.handleForm(newcard)
      }
    })
    this.resetConfig(_group)
  }
  resetConfig = (group) => {
    let _config = fromJS(this.state.config).toJS()
    if (group.floor === 1) {
      _config = group
    } else {
      _config.fields = list
      this.setState({config: _config})
      _config.groups = _config.groups.map(item => {
        if (item.uuid === group.uuid) return group
        return item
      })
    }
    this.setState({
      config: _config
    })
  }
  /**
   * @description 表单编辑
   * 1、显示编辑弹窗-visible
   * 2、保存编辑项-card
   * 3、设置编辑参数项-formlist
   */
  handleForm = (_card) => {
    const { componentConfig } = this.props
    const { config } = this.state
    let card = fromJS(_card).toJS()
    let _inputfields = []
    let _tabfields = []
    let _linkableFields = []
    let _linksupFields = [{
      value: '',
      text: '空'
    }]
    let standardform = null
    _inputfields = config.fields.filter(item => item.type === 'text' || item.type === 'number' || item.type === 'textarea' || item.type === 'color')
    _tabfields = config.fields.filter(item => card.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type))
    _tabfields.unshift({field: '', text: '原表单'})
    let uniq = new Map()
    uniq.set(card.field, true)
    let index = null
    config.fields.forEach((item, i) => {
      if (card.uuid === item.uuid) {
        index = i
      }
      if (item.type !== 'select' && item.type !== 'link' && item.type !== 'radio') return
      if (item.field && !uniq.has(item.field)) {
        uniq.set(item.field, true)
        _linkableFields.push({
          value: item.field,
          text: item.label + ' (表单)'
        })
        _linksupFields.push({
          value: item.field,
          text: item.label
        })
      }
    })
    if (index !== null) {
      if (index === 0) {
        standardform = config.fields[index + 1] || null
      } else {
        standardform = config.fields[index - 1] || null
      }
    }
    componentConfig.columns.forEach(col => {
      if (col.field && !uniq.has(col.field)) {
        uniq.set(col.field, true)
        _linkableFields.push({
          value: col.field,
          text: col.label + ' (显示列)'
        })
      }
    })
    if (card.linkSubField && card.linkSubField.length > 0) {
      let fields = _inputfields.map(item => item.field)
      card.linkSubField = card.linkSubField.filter(item => fields.includes(item))
    }
    if (!card.span && standardform && standardform.span) {
      card.span = standardform.span
      card.labelwidth = standardform.labelwidth
    }
    this.setState({
      standardform,
      visible: true,
      card: card,
      formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields, !!this.props.editTab)
      formlist: getSearchForm(card, [])
    })
  }
@@ -182,15 +149,12 @@
   */
  handleSubmit = () => {
    this.formRef.handleConfirm().then(res => {
      let _config = fromJS(this.state.config).toJS()
      let _group = fromJS(this.state.group).toJS()
      let fieldrepet = false // 字段重复
      let labelrepet = false // 提示文字重复
      _config.fields = _config.fields.map(item => {
        if (item.uuid !== res.uuid && res.field && item.field && item.field.toLowerCase() === res.field.toLowerCase()) {
      _group.fields = _group.fields.map(item => {
        if (item.uuid !== res.uuid && item.field.toLowerCase() === res.field.toLowerCase()) {
          fieldrepet = true
        } else if (res.label && item.uuid !== res.uuid && item.label === res.label) {
          labelrepet = true
        }
        if (item.uuid === res.uuid) {
@@ -207,18 +171,9 @@
          duration: 10
        })
        return
      } else if (labelrepet) {
        notification.warning({
          top: 92,
          message: '名称已存在!',
          duration: 10
        })
        return
      }
      _config.fields = _config.fields.filter(item => !item.origin)
      if (['select', 'multiselect', 'link', 'checkbox', 'radio', 'checkcard'].includes(res.type) && res.resourceType === '1' && /\s/.test(res.dataSource)) {
      if (['checkcard'].includes(res.type) && res.resourceType === '1' && /\s/.test(res.dataSource)) {
        this.setState({
          sqlVerifing: true
        })
@@ -243,10 +198,11 @@
          if (result.status) {
            this.setState({
              sqlVerifing: false,
              config: _config,
              group: _group,
              card: null,
              visible: false
            })
            this.resetConfig(_group)
          } else {
            this.setState({sqlVerifing: false})
            
@@ -257,10 +213,11 @@
        })
      } else {
        this.setState({
          config: _config,
          group: _group,
          card: null,
          visible: false
        })
        this.resetConfig(_group)
      }
    })
  }
@@ -274,12 +231,13 @@
    confirm({
      content: `确定删除${card.label ? `<<${card.label}>>` : ''}吗?`,
      onOk() {
        let _config = fromJS(_this.state.config).toJS()
        _config.fields = _config.fields.filter(item => !(item.uuid === card.uuid))
        let _group = fromJS(_this.state.group).toJS()
        _group.fields = _group.fields.filter(item => item.uuid !== card.uuid)
        _this.setState({
          config: _config,
          group: _group,
        })
        _this.resetConfig(_group)
      },
      onCancel() {}
    })
@@ -315,7 +273,7 @@
  }
  /**
   * @description 全局设置模态框
   * @description 分组设置模态框
   */
  changeSetting = () => {
    this.setState({
@@ -324,30 +282,32 @@
  }
  /**
   * @description 保存全局设置
   * @description 保存分组设置
   */
  settingSave = () => {
    const {config} = this.state
    this.settingRef.handleConfirm().then(res => {
      let _group = {...this.state.group, setting: res}
      this.setState({
        config: {...config, setting: res},
        group: _group,
        settingVisible: false
      })
      this.resetConfig(_group)
    })
  }
  editModalCancel = () => {
    const { config, card } = this.state
    const { group, card } = this.state
    if (card.focus) {
      let _fields = config.fields.filter(item => item.uuid !== card.uuid)
      let _config = {...config, fields: _fields}
      let _fields = group.fields.filter(item => item.uuid !== card.uuid)
      let _group = {...group, fields: _fields}
      this.setState({
        card: null,
        config: _config,
        group: _group,
        visible: false
      })
      this.resetConfig(_group)
    } else {
      this.setState({
        card: null,
@@ -356,18 +316,77 @@
    }
  }
  /**
   * @description 更新
  plusGroup = () => {
    let config = fromJS(this.state.config).toJS()
    let _g = {
      uuid: Utils.getuuid(),
      wrap: { name: 'name', icon: '' },
      setting: { type: 'title', field: '', title: '', focus: 'true', btn: 'hidden' },
      fields: []
    }
    config.groups.push(_g)
    this.setState({config, group: config, editGroup: _g, gVisible: true})
  }
  handleGroupForm = (_g) => {
    this.setState({editGroup: _g, gVisible: true})
  }
   /**
   * @description 保存分组设置
   */
  updateConfig = (config) => {
  groupSave = () => {
    this.gRef.handleConfirm().then(res => {
      let _g = {...this.state.editGroup, wrap: res}
      let _group = fromJS(this.state.group).toJS()
      _group.groups = _group.groups.map(item => {
        if (item.uuid === _g.uuid) return _g
        return item
      })
      this.setState({
        editGroup: null,
        group: _group,
        gVisible: false
      })
      this.resetConfig(_group)
    })
  }
  handleGroup = (g) => {
    this.setState({
      config
      group: g
    })
  }
  closeGroup = (g) => {
    const _this = this
    let _group = fromJS(this.state.group).toJS()
    _group.groups = _group.groups.filter(item => item.uuid !== g.uuid)
    confirm({
      content: `确定删除分组《${g.wrap.name}》吗?`,
      onOk() {
        _this.setState({ group: _group })
        _this.resetConfig(_group)
      },
      onCancel() {}
    })
  }
  returnUp = () => {
    const { config } = this.state
    this.setState({
      group: config
    })
  }
  render () {
    const { config, dict } = this.state
    const { dict, group, editGroup } = this.state
    return (
      <div className="mob-search-board">
        <DndProvider backend={HTML5Backend}>
@@ -379,38 +398,48 @@
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                <FieldsComponent
                  config={config}
                  type="form"
                  updatefield={this.updateConfig}
                />
              </Panel>
            </Collapse>
          </div>
          <div className="modal-control">
            <Button icon="setting" onClick={this.changeSetting}>设置</Button>
            <Button type="primary" onClick={this.submitConfig}>保存</Button>
            <Button onClick={this.cancelConfig}>返回</Button>
            <PasteComponent type="menu" Tab={null} insert={this.insert} />
            <Button onClick={this.cancelConfig}>关闭</Button>
            {!group.floor ? <Button onClick={this.returnUp}>返回上级</Button> : null}
            <PasteComponent insert={this.insert} />
            <Switch checkedChildren={dict['model.switch.open']} unCheckedChildren={dict['model.switch.close']} defaultChecked={this.state.showField} onChange={(val) => this.setState({showField: val})} />
          </div>
          <div className="setting">
            <div className="mob-shell" style={{width: window.GLOB.shellWidth, height: window.GLOB.shellHeight}}>
              <div className="mob-shell-inner">
                <div className="am-navbar">
                <Icon className="setting-group" onClick={this.changeSetting} type="setting" />
                {group.setting.type === 'title' ? <div className="am-navbar">
                  <Icon type="close" />
                  <div className="am-navbar-title">{config.setting.title}</div>
                  <div className="am-navbar-title">{group.setting.title}</div>
                </div> : <div className="am-navbar">
                  <Icon type="left" />
                  <div className="search-bar"><Icon type="search" /></div>
                </div>}
                {group.floor === 1 ? <Icon className="plus-group" type="plus" onClick={this.plusGroup} /> : null}
                <div style={{minHeight: 'calc(100% - 100px)'}}>
                  {group.floor === 1 && group.groups.length > 0 ? <GDragElement
                    list={group.groups}
                    handleList={this.handleGroupList}
                    handleForm={this.handleGroupForm}
                    handleGroup={this.handleGroup}
                    closeForm={this.closeGroup}
                  /> : null}
                  <DragElement
                    list={group.fields}
                    showField={this.state.showField}
                    handleList={this.handleList}
                    handleForm={this.handleForm}
                    closeForm={this.closeForm}
                  />
                </div>
                <DragElement
                  list={config.fields}
                  setting={config.setting}
                  showField={this.state.showField}
                  placeholder={this.state.dict['header.form.modal.placeholder']}
                  handleList={this.handleList}
                  handleForm={this.handleForm}
                  closeForm={this.closeForm}
                />
                <Button className="modal-submit" type="primary">确定</Button>
                {group.setting.btn !== 'hidden' ? <div className="search-btn">
                  <Button className="reset">重置</Button>
                  <Button className="submit" type="primary">确定</Button>
                </div> : null}
              </div>
            </div>
          </div>
@@ -424,12 +453,11 @@
          confirmLoading={this.state.sqlVerifing}
          destroyOnClose
        >
          <ModalForm
          <SearchForm
            dict={this.state.dict}
            card={this.state.card}
            formlist={this.state.formlist}
            inputSubmit={this.handleSubmit}
            standardform={this.state.standardform}
            wrappedComponentRef={(inst) => this.formRef = inst}
          />
        </Modal>
@@ -443,13 +471,26 @@
          destroyOnClose
        >
          <SettingForm
            config={config}
            dict={this.state.dict}
            isSubTab={!!this.props.editTab}
            config={group}
            inputSubmit={this.settingSave}
            wrappedComponentRef={(inst) => this.settingRef = inst}
          />
        </Modal>
        <Modal
          title={this.state.dict['model.edit']}
          visible={this.state.gVisible}
          width={600}
          maskClosable={false}
          onOk={this.groupSave}
          onCancel={() => { this.setState({ gVisible: false }) }}
          destroyOnClose
        >
          <GroupForm
            config={editGroup}
            inputSubmit={this.groupSave}
            wrappedComponentRef={(inst) => this.gRef = inst}
          />
        </Modal>
      </div>
    )
  }
src/mob/searchconfig/index.scss
@@ -156,28 +156,72 @@
        position: relative;
        border-bottom: 1px solid #f0f0f0;
        margin-bottom: 10px;
        .anticon-close {
        .anticon-close, .anticon-left {
          position: absolute;
          line-height: 45px;
          font-size: 18px;
          right: 10px;
          left: 10px;
        }
        .am-navbar-title {
          font-size: 16px;
          text-align: center;
          color: #000000;
        }
        .search-bar {
          height: 35px;
          line-height: 35px;
          padding: 0 10px;
          color:rgba(0, 0, 0, 0.65);
          border: 1px solid #E9EDF0;
          background: #E9EDF0;
          border-radius: 4px;
          position: relative;
          top: 50%;
          transform: translateY(-50%);
          margin: 0 30px 0 35px;
        }
      }
      .modal-submit {
        width: 100%;
        border-radius: 0;
        opacity: 0.5;
        cursor: default;
        font-size: 18px;
        height: 40px;
      .search-btn {
        .reset {
          width: 30%;
          border-radius: 0;
          opacity: 0.5;
          cursor: default;
          font-size: 18px;
          height: 45px;
        }
        .submit {
          width: 70%;
          border-radius: 0;
          opacity: 0.5;
          cursor: default;
          font-size: 18px;
          height: 45px;
        }
      }
      .modal-fields-row {
        min-height: calc(100% - 95px);
      .setting-group {
        position: absolute;
        right: 0px;
        top: 0px;
        font-size: 18px;
        padding: 5px;
        cursor: pointer;
        z-index: 1;
      }
      .plus-group {
        color: #26C281;
        position: absolute;
        right: 0px;
        top: 45px;
        font-size: 18px;
        padding: 5px;
        cursor: pointer;
        z-index: 2;
      }
      .modal-search-fields-row {
        min-height: 300px;
        padding: 0 10px 35px;
      }
src/mob/searchconfig/pastecomponent/index.jsx
@@ -9,7 +9,6 @@
class PasteController extends Component {
  static propTpyes = {
    config: PropTypes.object,        // 组件配置
    updateConfig: PropTypes.func
  }
@@ -18,36 +17,18 @@
  }
  pasteSubmit = () => {
    const { config } = this.props
    this.pasteFormRef.handleConfirm().then(res => {
      if (res.copyType !== 'form') {
      if (res.copyType !== 'search') {
        notification.warning({ top: 92, message: '配置信息格式错误!', duration: 5 })
        return
      }
      let keys = config.fields.map(item => item.field ? item.field.toLowerCase() : '$emp_ty$')
      if (['multiselect', 'color', 'brafteditor'].includes(res.type)) {
        res.type = 'text'
      }
      if (res.field && keys.includes(res.field.toLowerCase())) {
        notification.warning({
          top: 92,
          message: '搜索字段已存在!',
          duration: 5
        })
        return
      }
      this.props.updateConfig(config)
      this.props.updateConfig(res)
      this.setState({visible: false})
      notification.success({
        top: 92,
        message: '粘贴成功!',
        duration: 2
      })
    })
  }
src/mob/searchconfig/searchdragelement/card.jsx
New file
@@ -0,0 +1,132 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Icon, Popover, Form } from 'antd'
import { Range } from 'antd-mobile'
import moment from 'moment'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const CheckCard = asyncComponent(() => import('@/templates/modalconfig/checkCard'))
const Card = ({ id, card, moveCard, findCard, editCard, closeCard, copyCard, showField }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'search', id, originalIndex },
    collect: monitor => ({
      isDragging: monitor.isDragging(),
    }),
  })
  const [, drop] = useDrop({
    accept: 'search',
    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 formItem = null
  if (card.type === 'date') {
    formItem = (<div className="am-list-item">
      <div className="am-list-line">
        {card.labelShow !== 'false' ? <div className="am-input-label">{card.label}</div> : null}
        <div className="am-input-control">{card.initval ? moment().subtract(card.initval, 'days').format('YYYY-MM-DD') : '请选择'}</div>
        <div className="am-list-extra"><Icon type="right" /></div>
      </div>
    </div>)
  } else if (card.type === 'datemonth') {
    formItem = (<div className="am-list-item">
      <div className="am-list-line">
        {card.labelShow !== 'false' ? <div className="am-input-label">{card.label}</div> : null}
        <div className="am-input-control">{card.initval ? moment().subtract(card.initval, 'month').format('YYYY-MM') : '请选择'}</div>
        <div className="am-list-extra"><Icon type="right" /></div>
      </div>
    </div>)
  } else if (card.type === 'range') {
    let value = [(card.minValue || 0), (card.maxValue || 20)]
    if (card.initval) {
      value = card.initval.split(',')
      value = [+value[0], +value[1]]
    }
    formItem = (<div className="am-list-item slider">
      <div className="am-list-line">
        {card.labelShow !== 'false' ? <div className="am-input-label">{card.label}</div> : null}
        <div className="am-input-control">
          <Range
            min={card.minValue || 0}
            max={card.maxValue || 20}
            value={value}
          />
        </div>
      </div>
    </div>)
  } else if (card.type === 'daterange') {
    let value = '请选择'
    if (card.initval) {
      try {
        let _initval = JSON.parse(card.initval)
        value = [moment().subtract(_initval[0], 'days').format('YYYY-MM-DD'), moment().subtract(_initval[1], 'days').format('YYYY-MM-DD')].join(' ~ ')
      } catch {
        value = '请选择'
      }
    }
    formItem = (<div className="am-list-item">
      <div className="am-list-line">
        {card.labelShow !== 'false' ? <div className="am-input-label">{card.label}</div> : null}
        <div className="am-input-control">{value}</div>
        <div className="am-list-extra"><Icon type="right" /></div>
      </div>
    </div>)
  } else if (card.type === 'checkcard') {
    formItem = (<div className="am-list-item check-card">
      <div className="am-list-line">
        {card.labelShow !== 'false' ? <div className="am-input-label">{card.label}</div> : null}
        <div className="am-input-control">
          <CheckCard config={card} />
        </div>
      </div>
    </div>)
  }
  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))}>
          {card.type === 'split' ? formItem : <Form.Item
            className={'ant-form-item' + (card.required === 'true' ? ' required' : '') + (card.splitline === 'false' ? ' no-boder' : '')}
          >
            {formItem}
            {showField ? <div className="field-name">{card.field}{card.hidden === 'true' ? '(隐藏)' : ''}</div> : ''}
          </Form.Item>}
        </div>
      </div>
    </Popover>
  )
}
export default Card
src/mob/searchconfig/searchdragelement/index.jsx
New file
@@ -0,0 +1,123 @@
import React, { useState } from 'react'
import { useDrop } from 'react-dnd'
import { is, fromJS } from 'immutable'
import update from 'immutability-helper'
import Utils from '@/utils/utils.js'
import Card from './card'
import './index.scss'
const Container = ({list, 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]] })
    handleList(_cards)
  }
  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)
    delete card.focus
    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 = 'search'
    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)
    handleList(_cards, _card)
  }
  const [, drop] = useDrop({
    accept: 'search',
    drop(item) {
      if (item.hasOwnProperty('originalIndex')) {
        return
      }
      let newcard = {}
      newcard.uuid = Utils.getuuid()
      newcard.label = 'label'
      newcard.type = item.subType
      newcard.resourceType = '0'
      newcard.options = []
      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)
      handleList(_cards, newcard)
    }
  })
  return (
    <div ref={drop} className="ant-row modal-search-fields-row">
      {cards.map(card => {
        return <Card
          id={card.uuid}
          key={card.uuid}
          card={card}
          showField={showField}
          moveCard={moveCard}
          editCard={editCard}
          closeCard={closeCard}
          copyCard={copyCard}
          findCard={findCard}
        />
      })}
    </div>
  )
}
export default Container
src/mob/searchconfig/searchdragelement/index.scss
New file
@@ -0,0 +1,148 @@
.modal-search-fields-row {
  padding-bottom: 35px;
  .mob-col.ant-col {
    display: inline-block;
    float: none;
    vertical-align: top;
    padding-left: 1.2%;
    padding-right: 1.2%;
  }
  .am-list-item {
    font-size: 16px;
    padding-left: 10px;
    position: relative;
    display: flex;
    height: 44px;
    min-height: 44px;
    background-color: #fff;
    vertical-align: middle;
    overflow: hidden;
    transition: background-color 200ms;
    align-items: center;
    .am-list-line {
      align-items: center;
      position: relative;
      display: flex;
      flex: 1 1;
      align-self: stretch;
      padding-right: 15px;
      overflow: hidden;
      .am-input-label {
        width: 28%;
        color: #000;
        font-size: 16px;
        margin-left: 0;
        margin-right: 5px;
        text-align: left;
        white-space: nowrap;
        overflow: hidden;
        padding: 2px 0;
        text-overflow: ellipsis;
      }
      .am-list-switch {
        flex: 1;
        text-align: right;
      }
      .am-input-control {
        font-size: 16px;
        flex: 1 1;
        text-align: right;
      }
      .am-input-control.left {
        text-align: left;
      }
      .am-list-extra {
        display: block;
        width: 15px;
        height: 15px;
        margin-left: 8px;
        i {
          vertical-align: top;
        }
      }
    }
  }
  .am-list-item.check-card {
    height: auto;
    .am-list-line {
      align-items: start;
      display: block;
      .check-card-edit-box {
        margin-top: 0!important;
      }
    }
  }
  .am-list-item.hint {
    height: auto;
    .am-list-line {
      align-items: start;
      display: block;
      .am-input-label {
        line-height: 2;
      }
      .am-input-control {
        font-size: 14px;
        line-height: 1.5;
        text-align: left;
        padding-bottom: 5px;
      }
    }
  }
  .am-list-item.slider {
    height: auto;
    .am-list-line {
      align-items: start;
      display: block;
      .am-input-control {
        height: 25px;
        padding-top: 10px;
      }
    }
  }
  .check-card-edit-box .card-cell span {
    line-height: 1.5;
  }
  .ant-form-item {
    cursor: move;
    margin-bottom: 0px;
    .ant-form-item-control-wrapper::after {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      opacity: 0;
      z-index: 1;
    }
    .field-name {
      margin-left: 10px;
      line-height: 1.5;
    }
    .ant-form-item-children {
      vertical-align: top;
    }
  }
  .ant-form-item.required {
    .am-input-label::before {
      display: inline-block;
      margin-right: 4px;
      color: #f5222d;
      font-size: 14px;
      font-family: SimSun, sans-serif;
      line-height: 1;
      content: '*';
    }
  }
  .ant-form-item.no-boder {
    .am-list-line {
      border-bottom: none;
    }
  }
  .page-card {
    margin-bottom: 10px;
  }
}
src/mob/searchconfig/settingform/index.jsx
New file
@@ -0,0 +1,192 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, Select } from 'antd'
import { formRule } from '@/utils/option.js'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,     // 字典项
    config: PropTypes.object,   // 表单配置信息
    inputSubmit: PropTypes.any  // 回车提交事件
  }
  state = {
    roleList: [],
    type: this.props.config.setting.type || 'title'
  }
  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 { config } = this.props
    const { getFieldDecorator } = this.props.form
    const { type, roleList } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout} className="ant-advanced-search-form modal-setting-form">
        <Row gutter={24}>
          <Col span={12}>
            <Form.Item label="顶部样式">
              {getFieldDecorator('type', {
                initialValue: type
              })(
                <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => this.setState({type: e.target.value})}>
                  <Radio value="title">标题栏</Radio>
                  <Radio value="search">搜索栏</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
          {type === 'title' ? <Col span={12}>
            <Form.Item label="标题">
              {getFieldDecorator('title', {
                initialValue: config.setting.title,
                rules: [
                  {
                    max: formRule.input.max,
                    message: formRule.input.message
                  }
                ]
              })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />)}
            </Form.Item>
          </Col> : null}
          {type === 'search' ? <Col span={12}>
            <Form.Item label="搜索字段">
              {getFieldDecorator('field', {
                initialValue: config.setting.field,
                rules: [
                  {
                    required: true,
                    message: '请填写字段!'
                  },
                  {
                    max: formRule.input.max,
                    message: formRule.input.message
                  },
                  {
                    pattern: formRule.field.multipattern,
                    message: formRule.field.message
                  }
                ]
              })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />)}
            </Form.Item>
          </Col> : null}
          {type === 'search' ? <Col span={12}>
            <Form.Item label="初始值">
              {getFieldDecorator('initval', {
                initialValue: config.setting.initval,
                rules: [
                  {
                    max: formRule.input.max,
                    message: formRule.input.message
                  }
                ]
              })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />)}
            </Form.Item>
          </Col> : null}
          {type === 'search' ? <Col span={12}>
            <Form.Item label="必填">
              {getFieldDecorator('required', {
                initialValue: config.setting.required || 'false'
              })(
                <Radio.Group>
                  <Radio value="true">是</Radio>
                  <Radio value="false">否</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null}
          {type === 'search' ? <Col span={12}>
            <Form.Item label="自动聚焦">
              {getFieldDecorator('focus', {
                initialValue: config.setting.focus || 'true'
              })(
                <Radio.Group>
                  <Radio value="true">是</Radio>
                  <Radio value="false">否</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null}
          <Col span={12}>
            <Form.Item label="搜索按钮">
              {getFieldDecorator('btn', {
                initialValue: config.setting.btn || 'hidden'
              })(
                <Radio.Group>
                  <Radio value="hidden">隐藏</Radio>
                  <Radio value="show">显示</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
          {type === 'search' ? <Col span={12}>
            <Form.Item label="黑名单">
              {getFieldDecorator('blacklist', {
                initialValue: config.setting.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> : null}
        </Row>
      </Form>
    )
  }
}
export default Form.create()(SettingForm)
src/mob/searchconfig/settingform/index.scss
New file
@@ -0,0 +1,18 @@
.ant-advanced-search-form.modal-setting-form {
  .textarea {
    .ant-form-item-label {
      width: 16.3%;
    }
    .ant-form-item-control-wrapper {
      width: 83.33333333%;
    }
  }
  .ant-input-number {
    width: 100%;
  }
  .anticon-question-circle {
    color: #c49f47;
    position: relative;
    left: -3px;
  }
}
src/mob/searchconfig/source.jsx
@@ -1,104 +1,33 @@
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
export const SearchItems = [
  {
    type: 'form',
    label: CommonDict['model.form.text'],
    subType: 'text',
    type: 'search',
    label: '数值(区间)',
    subType: 'range',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.number'],
    subType: 'number',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.select'],
    subType: 'select',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.link'],
    subType: 'link',
    url: ''
  },
  {
    type: 'form',
    label: '开关',
    subType: 'switch',
    url: ''
  },
  {
    type: 'form',
    label: '多选框',
    subType: 'checkbox',
    url: ''
  },
  {
    type: 'form',
    label: '单选框',
    subType: 'radio',
    url: ''
  },
  {
    type: 'form',
    type: 'search',
    label: '选项卡',
    subType: 'checkcard',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['header.form.fileupload'],
    subType: 'fileupload',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.dateday'],
    type: 'search',
    label: '日期(天)',
    subType: 'date',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.datemonth'],
    type: 'search',
    label: '日期(月)',
    subType: 'datemonth',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.datetime'],
    subType: 'datetime',
    type: 'search',
    label: '日期(区间)',
    subType: 'daterange',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['model.form.textarea'],
    subType: 'textarea',
    url: ''
  },
  {
    type: 'form',
    label: CommonDict['header.form.funcvar'],
    subType: 'funcvar',
    url: ''
  },
  {
    type: 'form',
    label: '提示',
    subType: 'hint',
    url: ''
  },
  {
    type: 'form',
    label: '分割线',
    subType: 'split',
    url: ''
  }
]
src/templates/formtabconfig/index.jsx
@@ -16,23 +16,25 @@
import TabsComponent from '@/templates/sharecomponent/tabscomponent'
import ModalForm from '@/templates/zshare/modalform'
import PasteForm from '@/templates/zshare/pasteform'
import ActionForm from './actionform'
import SettingForm from './settingform'
import DragElement from './dragelement'
import GroupForm from './groupform'
import EditCard from '@/templates/zshare/editcard'
import VerifyCard from '@/templates/zshare/verifycard'
import MenuForm from '@/templates/zshare/menuform'
import SourceElement from '@/templates/zshare/dragsource'
import CreateFunc from '@/templates/zshare/createfunc'
import asyncComponent from '@/utils/asyncComponent'
import Source from './source'
import './index.scss'
const { Panel } = Collapse
const { Option } = Select
const { confirm } = Modal
const ModalForm = asyncComponent(() => import('@/templates/zshare/modalform'))
const CreateFunc = asyncComponent(() => import('@/templates/zshare/createfunc'))
const VerifyCard = asyncComponent(() => import('@/templates/zshare/verifycard'))
class ComTableConfig extends Component {
  static propTpyes = {
src/templates/modalconfig/index.jsx
@@ -13,7 +13,6 @@
import enUS from '@/locales/en-US/model.js'
import { getModalForm } from '@/templates/zshare/formconfig'
import ModalForm from '@/templates/zshare/modalform'
import SourceElement from './dragelement/source'
import SettingForm from './settingform'
import MenuForm from './menuform'
@@ -25,6 +24,7 @@
const { Panel } = Collapse
const { confirm } = Modal
const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
const ModalForm = asyncComponent(() => import('@/templates/zshare/modalform'))
const EditComponent = asyncComponent(() => import('@/templates/zshare/editcomponent'))
const DragElement = asyncComponent(() => import('./dragelement'))
const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
src/templates/sharecomponent/searchcomponent/searchform/index.jsx
@@ -96,7 +96,8 @@
  dateweek: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced'],
  datemonth: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced'],
  daterange: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced'],
  group: ['label', 'type', 'field', 'datefield', 'initval', 'blacklist', 'ratio', 'items', 'required', 'labelShow']
  group: ['label', 'type', 'field', 'datefield', 'initval', 'blacklist', 'ratio', 'items', 'required', 'labelShow'],
  range: ['label', 'type', 'field', 'initval', 'blacklist', 'maxValue', 'minValue', 'step', 'labelShow']
}
class MainSearch extends Component {
@@ -174,7 +175,7 @@
            form.options = matchReg.date
          } else if (type === 'datemonth') {
            form.options = matchReg.datemonth
          } else if (type === 'dateweek' || type === 'daterange') {
          } else if (type === 'dateweek' || type === 'daterange' || type === 'range') {
            form.options = matchReg.daterange
          }
        } else if (form.key === 'field' && (type === 'text' || type === 'select')) {
@@ -265,7 +266,7 @@
              form.options = matchReg.date
            } else if (value === 'datemonth') {
              form.options = matchReg.datemonth
            } else if (value === 'dateweek' || value === 'daterange') {
            } else if (value === 'dateweek' || value === 'daterange' || value === 'range') {
              form.options = matchReg.daterange
            }
            matchs = form.options
@@ -443,14 +444,17 @@
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal || 6,
                initialValue: item.initVal,
                rules: [
                  {
                    required: item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<InputNumber min={item.min} max={item.max} precision={0} onPressEnter={this.handleSubmit}/>)}
              })(item.max ?
                  <InputNumber min={item.min} max={item.max} precision={0} onPressEnter={this.handleSubmit}/> :
                  <InputNumber onPressEnter={this.handleSubmit}/>
                )}
            </Form.Item>
          </Col>
        )
@@ -652,6 +656,47 @@
            values.options = []
          }
          if (values.type === 'range') {
            let error = ''
            if (values.maxValue <= values.minValue) {
              error = '最大值必须大于最小值'
            } else if (values.step <= 0) {
              error = '步长必须大于0'
            } else {
              let s = (values.maxValue - values.minValue) / values.step
              if (s !== parseInt(s)) {
                error = '步长必须被 (max - min) 整除'
              }
            }
            if (!error && values.initval) {
              let vals = values.initval.split(',')
              if (vals.length !== 2) {
                error = '初始值设置错误!'
              } else if (isNaN(parseFloat(vals[0])) || isNaN(parseFloat(vals[1]))) {
                error = '初始值设置错误!'
              } else {
                let start = parseFloat(vals[0])
                let end = parseFloat(vals[1])
                let s = (values.maxValue - start) / values.step
                let e = (values.maxValue - end) / values.step
                if (start > end || start < values.minValue || end > values.maxValue) {
                  error = '初始值设置错误!'
                } else if (s !== parseInt(s) || e !== parseInt(e)) {
                  error = '初始值设置错误!'
                }
              }
            }
            if (error) {
              notification.warning({
                top: 92,
                message: error,
                duration: 5
              })
              return
            }
          }
          if (isvalid) {
            ['linkField', 'valueField', 'valueText', 'orderBy'].forEach(item => {
              if (values[item]) {
src/templates/zshare/editcomponent/index.jsx
@@ -141,26 +141,10 @@
            config: _config
          })
        })
      } else if (options.includes('search') && (res.copyType === 'search' || res.copyType === 'form')) {
      } else if (options.includes('search') && res.copyType === 'search') {
        res.uuid = Utils.getuuid()
        _config.search = _config.search.filter(item => !item.origin)
        let keys = _config.search.map(item => item.field.toLowerCase())
        // search: text select multiselect link date dateweek datemonth daterange group
        // form: text number select multiselect link switch checkbox radio checkcard
        //       fileupload date datemonth datetime textarea hint color funcvar
        if (res.copyType === 'form') {
          if (['number', 'switch', 'textarea', 'fileupload', 'hint', 'color', 'funcvar'].includes(res.type)) {
            res.type = 'text'
          } else if (res.type === 'radio') {
            res.type = 'select'
          } else if (res.type === 'checkbox') {
            res.type = 'multiselect'
          } else if (res.type === 'datetime') {
            res.type = 'date'
          }
        }
        res.copyType = 'search'
        _config.search.push(res)
@@ -206,20 +190,10 @@
            config: _config
          })
        })
      } else if (options.includes('form') && (res.copyType === 'form' || res.copyType === 'search')) {
      } else if (options.includes('form') && res.copyType === 'form') {
        let fields = []
        let labels = []
        res.uuid = Utils.getuuid()
        // search: text select multiselect link date dateweek datemonth daterange group
        // form: text number select multiselect link switch checkbox radio checkcard
        //       fileupload date datemonth datetime textarea hint color funcvar
        if (res.copyType === 'search') {
          if (res.type === 'dateweek' || res.type === 'daterange' || res.type === 'group') {
            res.type = 'date'
          }
        }
        res.copyType = 'form'
        _config.fields.forEach(item => {
          item.field && fields.push(item.field.toLowerCase())
src/templates/zshare/formconfig.jsx
@@ -288,6 +288,7 @@
 */
export function getSearchForm (card, linkableFields) {
  let roleList = sessionStorage.getItem('sysRoles')
  let appType = sessionStorage.getItem('appType')
  if (roleList) {
    try {
      roleList = JSON.parse(roleList)
@@ -300,10 +301,10 @@
  let typeOptions = []
  if (sessionStorage.getItem('appType') === 'mob') {
  if (appType === 'mob') {
    typeOptions = [{
      value: 'text',
      text: Formdict['model.form.text']
      value: 'range',
      text: '数值(区间)'
    }, {
      value: 'checkcard',
      text: '选项卡'
@@ -313,10 +314,10 @@
    }, {
      value: 'datemonth',
      text: Formdict['model.form.datemonth']
    }, {
      value: 'daterange',
      text: Formdict['model.form.daterange']
    }]
    if (!['text', 'checkcard', 'date', 'datemonth'].includes(card.type)) {
      card.type = 'text'
    }
  } else {
    typeOptions = [{
      value: 'text',
@@ -349,6 +350,18 @@
      value: 'group',
      text: Formdict['model.form.dategroup']
    }]
  }
  if (card.focus) {
    if (['text', 'multiselect'].includes(card.type)) {
      card.match = 'like'
    } else if (['select', 'link', 'checkcard'].includes(card.type)) {
      card.match = '='
    } else if (card.type === 'date') {
      card.match = '>='
    } else if (['datemonth', 'dateweek', 'daterange', 'range'].includes(card.type)) {
      card.match = 'between'
    }
  }
  return [
@@ -389,7 +402,7 @@
      type: 'text',
      key: 'initval',
      label: Formdict['header.form.initval'],
      tooltip: '类型为下拉菜单时,初始值应为数据的Value值(使用数据源时,应为《值·字段》的值)',
      tooltip: '类型为下拉菜单时,初始值应为数据的Value值(使用数据源时,应为《值·字段》的值);类型为数值(区间)时,初始值使用逗号拼接,例如 3,10',
      initVal: card.initval,
      required: false
    },
@@ -440,7 +453,7 @@
      min: 1,
      max: 24,
      precision: 0,
      label: '卡片宽度',
      label: '元素宽度',
      initVal: card.width || 4,
      tooltip: '栅格布局,每行等分为24列。',
      required: true
@@ -621,7 +634,8 @@
      max: 24,
      label: Formdict['header.form.ratio'],
      tooltip: '栅格布局,每行等分为24列。',
      initVal: card.ratio,
      initVal: card.ratio || 6,
      forbid: appType === 'mob',
      required: false
    },
    {
@@ -679,6 +693,31 @@
    //   }]
    // },
    {
      type: 'number',
      key: 'maxValue',
      label: '最大值',
      initVal: card.maxValue,
      forbid: appType !== 'mob',
      required: true
    },
    {
      type: 'number',
      key: 'minValue',
      label: '最小值',
      initVal: card.minValue,
      forbid: appType !== 'mob',
      required: true
    },
    {
      type: 'number',
      key: 'step',
      label: '步长',
      initVal: card.step,
      tooltip: '步长取值必须大于 0,并且可被 (max - min) 整除',
      forbid: appType !== 'mob',
      required: true
    },
    {
      type: 'radio',
      key: 'labelShow',
      label: '显示名称',
@@ -696,7 +735,7 @@
      key: 'advanced',
      label: '高级搜索',
      initVal: card.advanced || 'false',
      forbid: sessionStorage.getItem('appType') === 'mob',
      forbid: appType === 'mob',
      options: [{
        value: 'true',
        text: Formdict['model.true']
@@ -711,7 +750,7 @@
      label: '输入样式',
      initVal: card.inputType || 'input',
      required: false,
      forbid: sessionStorage.getItem('appType') === null,
      forbid: appType === null,
      options: [{
        value: 'input',
        text: '输入框'
@@ -2315,7 +2354,7 @@
      min: 1,
      max: 24,
      precision: 0,
      label: '卡片宽度',
      label: '元素宽度',
      initVal: card.width || 4,
      tooltip: '栅格布局,每行等分为24列。',
      required: true
@@ -2670,6 +2709,20 @@
      }]
    },
    {
      type: 'radio',
      key: 'arrange',
      label: '元素排列',
      initVal: card.arrange || 'adaptive',
      forbid: appType !== 'mob',
      options: [{
        value: 'line',
        text: '整行'
      }, {
        value: 'adaptive',
        text: '自适应'
      }]
    },
    {
      type: 'color',
      key: 'backgroundColor',
      label: '背景色',
src/templates/zshare/modalform/index.jsx
@@ -19,8 +19,8 @@
  text: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'regular', 'interception', 'span', 'labelwidth', 'tooltip', 'extra', 'enter', 'cursor', 'scan', 'splitline'],
  number: ['initval', 'readonly', 'hidden', 'decimal', 'min', 'max', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'enter', 'cursor', 'splitline'],
  select: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'setAll', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'emptyText', 'enter', 'splitline'],
  checkbox: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'span', 'labelwidth', 'tooltip', 'extra', 'splitline'],
  radio: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'setAll', 'emptyText', 'splitline'],
  checkbox: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'span', 'labelwidth', 'tooltip', 'extra', 'splitline', 'arrange'],
  radio: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'setAll', 'emptyText', 'splitline', 'arrange'],
  checkcard: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'linkSubField', 'fieldlength', 'span', 'labelwidth', 'display', 'tooltip', 'extra', 'width', 'multiple', 'borderColor', 'splitline'],
  multiselect: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'fieldlength', 'span', 'labelwidth', 'tooltip', 'extra'],
  link: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'setAll', 'linkField', 'span', 'labelwidth', 'tooltip', 'extra', 'emptyText', 'enter', 'splitline'],