king
2020-01-14 7ea1c5f53702951fc4df60e969fc67ef5d7af4dd
2020-01-14
16个文件已修改
4个文件已添加
1140 ■■■■ 已修改文件
src/locales/en-US/comtable.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/zh-CN/comtable.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/tableshare/mutilform/index.jsx 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/tableshare/mutilform/index.scss 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/dragelement/index.jsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/editable/index.jsx 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/editable/index.scss 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/index.jsx 201 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/index.scss 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/modalform/index.jsx 411 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/modalform/index.scss 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/source.jsx 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/dragelement/card.jsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/dragelement/index.jsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/dragelement/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/index.jsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/index.scss 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/dragelement/index.jsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/option.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/en-US/comtable.js
@@ -84,6 +84,9 @@
  'header.form.text': 'Text',
  'header.form.description': '描述',
  'header.form.textarea': '多行文本',
  'header.form.fileupload': '文件上传',
  'header.form.funcvar': '函数变量',
  'header.form.linkForm': '关联表单',
  'header.form.picture': '图片',
  'header.form.number': '数字',
  'header.form.colspan': '合并列',
src/locales/zh-CN/comtable.js
@@ -84,6 +84,9 @@
  'header.form.text': '文本',
  'header.form.description': '描述',
  'header.form.textarea': '多行文本',
  'header.form.fileupload': '文件上传',
  'header.form.funcvar': '函数变量',
  'header.form.linkForm': '关联表单',
  'header.form.picture': '图片',
  'header.form.number': '数字',
  'header.form.colspan': '合并列',
src/tabviews/tableshare/mutilform/index.jsx
@@ -8,6 +8,7 @@
import './index.scss'
const {MonthPicker} = DatePicker
const { TextArea } = Input
class MainSearch extends Component {
  static propTpyes = {
@@ -61,7 +62,7 @@
    let _inputfields = formlist.filter(item => item.type === 'text' || item.type === 'number') // 用于过滤下拉菜单关联表单
    formlist = formlist.map(item => {
      if (item.type === 'select' || item.type === 'link') {
      if (item.type === 'select' || item.type === 'link' || item.type === 'multiselect') {
        if (item.setAll === 'true') {
          item.options.unshift({
            key: Utils.getuuid(),
@@ -461,6 +462,32 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'textarea') {
        let _labelcol = cols !== 3 ? 8 / cols : 3
        let _wrapcol = cols !== 3 ? 16 + (cols - 1) * 4 : 21
        let _style = {}
        if (cols === 2) {
          _style.paddingLeft = '7px'
        }
        fields.push(
          <Col span={24} key={index} className="textarea-row" style={{..._style}}>
            <Form.Item label={item.label} labelCol={{xs: { span: 24 }, sm: { span: _labelcol }}} wrapperCol={ {xs: { span: 24 }, sm: { span: _wrapcol }} }>
              {getFieldDecorator(item.field, {
                initialValue: item.initval || '',
                rules: [
                  {
                    required: item.required === 'true',
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  },
                  {
                    max: formRule.textarea.max,
                    message: formRule.textarea.message
                  }
                ]
              })(<TextArea autosize={{ minRows: 2, maxRows: 6 }} disabled={item.readonly === 'true'} />)}
            </Form.Item>
          </Col>
        )
      }
    })
    
src/tabviews/tableshare/mutilform/index.scss
@@ -3,13 +3,23 @@
  padding: 0px 24px 20px;
  .ant-form-item {
    display: flex;
    // margin-bottom: 10px;
  }
  .ant-form-item-control-wrapper {
    flex: 1;
  }
  .ant-form-item-label {
    min-width: 100px;
    overflow: hidden;
    display: inline-block;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .textarea-row {
    .ant-col-sm-3 {
      width: 10.5%;
    }
    .ant-col-sm-21 {
      width: 89.5%;
    }
  }
  .ant-input-number {
    width: 100%;
src/templates/comtableconfig/index.jsx
@@ -354,7 +354,7 @@
          type: 'text',
          key: 'label',
          label: this.state.dict['header.form.name'],
          initVal: card.label,
          initVal: card.label || '',
          required: true,
          readonly: false
        },
@@ -362,7 +362,7 @@
          type: 'text',
          key: 'field',
          label: this.state.dict['header.form.field'],
          initVal: card.field,
          initVal: card.field || '',
          tooltip: '字段名可以使用逗号分隔,进行多字段综合搜索,注:综合搜索仅在文本类型时有效',
          tooltipClass: 'middle',
          required: true,
@@ -612,7 +612,7 @@
          type: 'select',
          key: 'pageTemplate',
          label: this.state.dict['header.form.pageTemplate'],
          initVal: card.pageTemplate,
          initVal: card.pageTemplate || '',
          required: true,
          options: []
        },
@@ -641,7 +641,7 @@
          type: 'text',
          key: 'innerFunc',
          label: this.state.dict['header.form.innerFunc'],
          initVal: card.innerFunc,
          initVal: card.innerFunc || '',
          tooltip: <div>
            <p>内部接口: 可自定义数据处理函数,函数名称需以{ableField}等字符开始;未设置时会调用系统函数,使用系统函数需完善数据源及操作类型;</p>
            <p>外部接口: 可自定义数据处理函数,提交数据经过内部函数处理后,传入外部接口,未设置时,数据会直接传入外部接口。</p>
@@ -669,7 +669,7 @@
          type: 'text',
          key: 'outerFunc',
          label: this.state.dict['header.form.outerFunc'],
          initVal: card.outerFunc,
          initVal: card.outerFunc || '',
          required: false,
          readonly: false
        },
@@ -677,7 +677,7 @@
          type: 'text',
          key: 'interface',
          label: this.state.dict['header.form.interface'],
          initVal: card.sysInterface === 'true' ? (window.GLOB.mainSystemApi || window.GLOB.subSystemApi) : card.interface,
          initVal: card.sysInterface === 'true' ? (window.GLOB.mainSystemApi || window.GLOB.subSystemApi) : (card.interface || ''),
          required: true,
          readonly: card.sysInterface === 'true'
        },
@@ -685,7 +685,7 @@
          type: 'text',
          key: 'callbackFunc',
          label: this.state.dict['header.form.callbackFunc'],
          initVal: card.callbackFunc,
          initVal: card.callbackFunc || '',
          required: false,
          readonly: false
        },
src/templates/formtabconfig/dragelement/index.jsx
@@ -63,6 +63,14 @@
  const [, drop] = useDrop({
    accept: ItemTypes[type],
    drop(item) {
      if (item.hasOwnProperty('originalIndex') && groupId) {
        const { card } = findCard(item.id)
        if (!card) {
          handleList(type, cards, null, groupId, item.id)
        }
      }
      if (item.hasOwnProperty('originalIndex')) {
        return
      }
@@ -134,7 +142,7 @@
      const _cards = update(cards, { $splice: [[targetIndex, 0, newcard]] })
      setCards(_cards)
      handleList(type, _cards, newcard)
      handleList(type, _cards, newcard, groupId)
      target = null
    }
  })
@@ -144,7 +152,7 @@
      {type === 'action' && cards.map(card => (
        <Card
          key={card.uuid}
          id={`${card.uuid}`}
          id={card.uuid}
          type={type}
          card={card}
          moveCard={moveCard}
@@ -159,7 +167,8 @@
      {type === 'search' && cards.map(card => (
        <Col key={card.uuid} span={24 / setting.cols}>
          <Card
            id={`${card.uuid}`}
            id={card.uuid}
            key={card.uuid}
            type={type}
            card={card}
            moveCard={moveCard}
src/templates/formtabconfig/editable/index.jsx
New file
@@ -0,0 +1,258 @@
import React, {Component} from 'react'
import { Table, Input, Button, Popconfirm, Form, Icon } from 'antd'
import Utils from '@/utils/utils.js'
import './index.scss'
const EditableContext = React.createContext()
const EditableRow = ({ form, index, ...props }) => (
  <EditableContext.Provider value={form}>
    <tr {...props} />
  </EditableContext.Provider>
)
const EditableFormRow = Form.create()(EditableRow)
class EditableCell extends Component {
  state = {
    editing: false
  }
  toggleEdit = () => {
    const editing = !this.state.editing
    this.setState({ editing }, () => {
      if (editing) {
        this.input.focus()
      }
    })
  }
  save = e => {
    const { record, handleSave } = this.props
    this.form.validateFields((error, values) => {
      handleSave({ ...record, ...values })
      if (error && error[e.currentTarget.id]) {
        return
      }
      this.toggleEdit()
      // handleSave({ ...record, ...values })
    })
  }
  renderCell = form => {
    this.form = form
    const { children, dataIndex, record } = this.props
    const { editing } = this.state
    return editing ? (
      <Form.Item style={{ margin: 0 }}>
        {form.getFieldDecorator(dataIndex, {
          rules: [
            {
              required: true,
              message: 'NOT NULL.',
            },
          ],
          initialValue: record[dataIndex]
        })(<Input ref={node => (this.input = node)} autoComplete="off" onPressEnter={this.save} onBlur={this.save} />)}
      </Form.Item>
    ) : (
      <div
        className="editable-cell-value-wrap"
        onClick={this.toggleEdit}
      >
        {children}
      </div>
    )
  }
  render() {
    const {
      editable,
      dataIndex,
      title,
      record,
      index,
      handleSave,
      children,
      ...restProps
    } = this.props
    return (
      <td {...restProps}>
        {editable ? (
          <EditableContext.Consumer style={{padding: 0}}>{this.renderCell}</EditableContext.Consumer>
        ) : (
          children
        )}
      </td>
    )
  }
}
class EditTable extends Component {
  constructor(props) {
    super(props)
    let columns = [
      {
        title: 'Value',
        dataIndex: 'Value',
        width: props.type === 'link' ? '27%' : '40%',
        editable: true
      },
      {
        title: 'Text',
        dataIndex: 'Text',
        width: props.type === 'link' ? '27%' : '40%',
        editable: true
      },
      {
        title: '操作',
        align: 'center',
        dataIndex: 'operation',
        render: (text, record) =>
          this.state.dataSource.length >= 1 ? (
            <Popconfirm title="Sure to delete?" onConfirm={() => this.handleDelete(record.key)}>
              <span style={{color: '#1890ff', cursor: 'pointer'}}><Icon type="delete" /></span>
            </Popconfirm>
          ) : null,
      }
    ]
    if (props.type === 'link') {
      columns.unshift({
        title: 'ParentID',
        dataIndex: 'ParentID',
        width: '27%',
        editable: true
      })
    }
    this.state = {
      columns: columns,
      dataSource: props.data,
      count: props.data.length,
      type: props.type
    }
  }
  handleDelete = key => {
    const dataSource = [...this.state.dataSource]
    this.setState({ dataSource: dataSource.filter(item => item.key !== key) })
  }
  handleAdd = () => {
    const { type, count, dataSource } = this.state
    const newData = {
      key: Utils.getuuid(),
      Value: `${count}`,
      Text: `${count}`
    }
    if (type === 'link') {
      newData.ParentID = `${count}`
    }
    this.setState({
      dataSource: [...dataSource, newData],
      count: count + 1
    })
  }
  handleSave = row => {
    const newData = [...this.state.dataSource]
    const index = newData.findIndex(item => row.key === item.key)
    const item = newData[index]
    newData.splice(index, 1, {
      ...item,
      ...row
    })
    this.setState({ dataSource: newData })
  }
  resetColumn = (type) => {
    let columns = [
      {
        title: 'Value',
        dataIndex: 'Value',
        width: type === 'link' ? '27%' : '40%',
        editable: true
      },
      {
        title: 'Text',
        dataIndex: 'Text',
        width: type === 'link' ? '27%' : '40%',
        editable: true
      },
      {
        title: '操作',
        align: 'center',
        dataIndex: 'operation',
        render: (text, record) =>
          this.state.dataSource.length >= 1 ? (
            <Popconfirm title="Sure to delete?" onConfirm={() => this.handleDelete(record.key)}>
              <span style={{color: '#1890ff', cursor: 'pointer'}}><Icon type="delete" /></span>
            </Popconfirm>
          ) : null,
      }
    ]
    if (type === 'link') {
      columns.unshift({
        title: 'ParentID',
        dataIndex: 'ParentID',
        width: '27%',
        editable: true
      })
    }
    this.setState({
      columns: columns,
      type: type
    })
  }
  UNSAFE_componentWillReceiveProps (nextProps) {
    if (this.props.type !== nextProps.type) {
      this.resetColumn(nextProps.type)
    }
  }
  render() {
    const { dataSource } = this.state
    const components = {
      body: {
        row: EditableFormRow,
        cell: EditableCell
      }
    }
    const columns = this.state.columns.map(col => {
      if (!col.editable) {
        return col
      }
      return {
        ...col,
        onCell: record => ({
          record,
          editable: col.editable,
          dataIndex: col.dataIndex,
          title: col.title,
          handleSave: this.handleSave,
        })
      }
    })
    return (
      <div className="common-modal-edit-table">
        <Button onClick={this.handleAdd} type="primary" className="add-row">
          添加
        </Button>
        <Table
          components={components}
          rowClassName={() => 'editable-row'}
          bordered
          dataSource={dataSource}
          columns={columns}
          pagination={false}
        />
      </div>
    )
  }
}
export default EditTable
src/templates/formtabconfig/editable/index.scss
New file
@@ -0,0 +1,36 @@
.common-modal-edit-table {
  .add-row {
    position: absolute;
    z-index: 1;
    right: 12px;
    top: -40px;
  }
  .ant-table-thead > tr > th {
    padding: 10px 16px;
  }
  .ant-table-tbody > tr > td {
    padding: 0px 16px;
  }
  .editable-cell-value-wrap {
    cursor: pointer;
    height: 40px;
    width: 100px;
    display: table-cell;
    vertical-align: middle;
    word-wrap: break-word;
    word-break: break-word;
    .ant-input {
      height: 30px;
      padding: 0 11px;
    }
  }
  .ant-form-item-control-wrapper {
    width: 100%;
  }
  .ant-table-placeholder {
    padding: 5px 16px;
    .ant-empty-normal {
      margin: 0;
    }
  }
}
src/templates/formtabconfig/index.jsx
@@ -11,7 +11,7 @@
import TabForm from './tabform'
import TabDragElement from './tabdragelement'
import Api from '@/api'
import SearchForm from '@/templates/tableshare/searchform'
import SearchForm from './modalform'
import DragElement from './dragelement'
import EditCard from '@/templates/tableshare/editcard'
import VerifyCard from '@/templates/tableshare/verifycard'
@@ -52,7 +52,6 @@
    card: null,              // 编辑元素
    searchloading: false,    // 搜索条件加载中
    actionloading: false,    // 按钮加载中
    columnsloading: false,   // 显示列加载中
    tabloading: false,       // 标签页加载中
    menuloading: false,      // 菜单保存中
    menucloseloading: false, // 菜单关闭时,选择保存
@@ -76,9 +75,6 @@
  UNSAFE_componentWillMount () {
    const { menu, editAction, config } = this.props
    console.log(menu)
    console.log(editAction)
    console.log(config)
    let _config = ''
    if (!config) {
@@ -240,8 +236,8 @@
    }
  }
  handleList = (type, list, card, groupId) => {
    const { config } = this.state
  handleList = (type, list, card, groupId, elementId) => {
    let config = JSON.parse(JSON.stringify(this.state.config))
    if (type === 'tabs') { // 标签页调整顺序或添加元素
      if (list.length > config[card.groupId].length) {
@@ -278,14 +274,77 @@
      } else {
        this.setState({config: {...config, action: list}})
      }
    } else if (type === 'search') {
      let _group = config.groups.filter(group => group.uuid === groupId)[0]
      let isChange = list.length > _group.length
      let isAdd = !elementId && list.length > _group.length
      if (isAdd) {
        _group.sublist = list.filter(item => !item.origin)
        this.handleSearch(card)
      } else if (elementId) {
        // 修改已有元素的分组
        let element = null
        config.groups.forEach(item => {
          item.sublist = item.sublist.filter(cell => {
            if (cell.uuid !== elementId) {
              return true
            } else {
              element = cell
              return false
            }
          })
        })
        _group.sublist.push(element)
      } else {
        _group.sublist = list
      }
      config.groups = config.groups.map(item => {
        if (item.uuid === _group.uuid) {
          return _group
        } else {
          return item
        }
      })
      if (isChange) {
        this.setState({
          searchloading: true,
          config: config
        }, () => {
          // 刷新对应的配置信息
          this.setState({
            searchloading: false
          })
        })
      } else {
        this.setState({
          config: config
        })
      }
    }
  }
  handleSearch = (card) => {
    const { config } = this.state
    let _inputfields = []
    // 设置下拉菜单可关联字段
    config.groups.forEach(group => {
      let sublist = group.sublist.filter(item => item.type === 'text' || item.type === 'number')
      _inputfields = [..._inputfields, ...sublist]
    })
    if (card.linkSubField && card.linkSubField.length > 0) {
      let fields = _inputfields.map(item => item.field)
      card.linkSubField = card.linkSubField.filter(item => fields.includes(item))
    }
    this.setState({
      visible: true,
      formtemp: 'search',
      modalTitle: '编辑-搜索条件',
      modalTitle: '编辑-表单',
      card: card,
      formlist: [
        {
@@ -301,8 +360,6 @@
          key: 'field',
          label: this.state.dict['header.form.field'],
          initVal: card.field,
          tooltip: '字段名可以使用逗号分隔,进行多字段综合搜索,注:综合搜索仅在文本类型时有效',
          tooltipClass: 'middle',
          required: true,
          readonly: false
        },
@@ -316,6 +373,9 @@
            value: 'text',
            text: this.state.dict['header.form.text']
          }, {
            value: 'number',
            text: this.state.dict['header.form.number']
          }, {
            value: 'select',
            text: this.state.dict['header.form.select']
          }, {
@@ -325,17 +385,20 @@
            value: 'link',
            text: this.state.dict['header.form.link']
          }, {
            value: 'fileupload',
            text: this.state.dict['header.form.fileupload']
          }, {
            value: 'date',
            text: this.state.dict['header.form.dateday']
          }, {
            value: 'dateweek',
            text: this.state.dict['header.form.dateweek']
          }, {
            value: 'datemonth',
            text: this.state.dict['header.form.datemonth']
          }, {
            value: 'daterange',
            text: this.state.dict['header.form.daterange']
            value: 'datetime',
            text: this.state.dict['header.form.datetime']
          }, {
            value: 'textarea',
            text: this.state.dict['header.form.textarea']
          }]
        },
        {
@@ -434,41 +497,58 @@
          }]
        },
        {
          type: 'select',
          key: 'match',
          label: this.state.dict['header.form.match'],
          initVal: card.match || 'like',
          required: true,
          type: 'number',
          key: 'decimal',
          label: this.state.dict['header.form.decimal'],
          initVal: card.decimal || 0,
          required: false
        },
        {
          type: 'number',
          key: 'min',
          label: '最小值',
          initVal: card.min || '',
          required: false
        },
        {
          type: 'number',
          key: 'max',
          label: '最大值',
          initVal: card.max || '',
          required: false
        },
        {
          type: 'radio',
          key: 'readonly',
          label: this.state.dict['header.form.readonly'],
          initVal: card.readonly || 'false',
          options: [{
            value: 'like',
            text: 'like'
            value: 'true',
            text: this.state.dict['header.form.true']
          }, {
            value: 'equal',
            text: 'equal'
          }, {
            value: 'greater',
            text: '>'
          }, {
            value: 'less',
            text: '<'
          }, {
            value: 'greaterequal',
            text: '>='
            value: 'false',
            text: this.state.dict['header.form.false']
          }]
        },
        {
          type: 'select',
          key: 'display',
          label: this.state.dict['header.form.display'],
          initVal: card.display || 'dropdown',
          required: true,
          type: 'radio',
          key: 'required',
          label: this.state.dict['header.form.field.required'],
          initVal: card.required || 'false',
          options: [{
            value: 'dropdown',
            text: this.state.dict['header.form.dropdown']
            value: 'true',
            text: this.state.dict['header.form.true']
          }, {
            value: 'button',
            text: this.state.dict['header.form.button']
            value: 'false',
            text: this.state.dict['header.form.false']
          }]
        },
        {
          type: 'multiselect',
          key: 'linkSubField',
          label: this.state.dict['header.form.linkForm'],
          initVal: card.linkSubField || [],
          options: _inputfields
        }
      ]
    })
@@ -809,8 +889,9 @@
        })
      }
      if (res.type !== 'tabs') {
        _config[res.type] = _config[res.type].map(item => {
      if (res.type === 'action') {
        _config.action = _config.action.map(item => {
          if (item.uuid === res.values.uuid) {
            isupdate = true
            return res.values
@@ -818,11 +899,25 @@
            return item
          }
        })
        _config[res.type] = _config[res.type].filter(item => !item.origin)
        _config.action = _config.action.filter(item => !item.origin)
  
        if (!isupdate) { // 操作不是修改,添加元素至列表
          _config[res.type].push(res.values)
          _config.action.push(res.values)
        }
      } else if (res.type === 'search') {
        _config.groups = _config.groups.map(item => {
          item.sublist = item.sublist.map(cell => {
            if (cell.uuid === res.values.uuid) {
              return res.values
            } else {
              return cell
            }
          })
          if (item.isDefault) {
            item.sublist = item.sublist.filter(cell => !cell.origin)
          }
          return item
        })
      } else { // 标签页的添加与修改
        _config[res.values.groupId] = _config[res.values.groupId].map(item => {
          if (item.uuid === res.values.uuid) {
@@ -843,14 +938,12 @@
        config: _config,
        searchloading: true,
        actionloading: true,
        columnsloading: true,
        tabloading: true,
        visible: false
      }, () => {
        this.setState({
          searchloading: false,
          actionloading: false,
          columnsloading: false,
          tabloading: false
        })
      })
@@ -1564,13 +1657,11 @@
              ParentID: res.parentId
            },
            searchloading: true,
            actionloading: true,
            columnsloading: true
            actionloading: true
          }, () => {
            this.setState({
              searchloading: false,
              actionloading: false,
              columnsloading: false
              actionloading: false
            })
          })
@@ -2004,11 +2095,9 @@
      this.setState({
        config: {...config, setting: res},
        settingVisible: false,
        columnsloading: true,
        tabloading: true
      }, () => {
        this.setState({
          columnsloading: false,
          tabloading: false
        })
      })
@@ -2269,13 +2358,13 @@
                />}
              </Panel>
              {/* 搜索条件添加 */}
              <Panel header={this.state.dict['header.menu.search']} key="1">
              <Panel header={this.state.dict['header.menu.form']} key="1">
                <div className="search-element">
                  {Source.searchItems.map((item, index) => {
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                <Button type="primary" block onClick={() => this.queryField('search')}>{this.state.dict['header.menu.search.add']}</Button>
                <Button type="primary" block onClick={() => this.queryField('search')}>{this.state.dict['header.menu.form.add']}</Button>
              </Panel>
              {/* 按钮添加 */}
              <Panel header={this.state.dict['header.menu.action']} key="2">
src/templates/formtabconfig/index.scss
@@ -173,13 +173,16 @@
          border-radius: 0;
          background: #1890ff;
          color: #ffffff;
          padding-left: 30px;
          padding-right: 20px;
          .anticon {
            font-size: 16px;
          }
          .ant-collapse-extra {
            .anticon-edit {
              position: absolute;
              right: 15px;
              left: 5px;
              top: 2px;
            }
          }
        }
src/templates/formtabconfig/modalform/index.jsx
New file
@@ -0,0 +1,411 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Select, Icon, Radio, notification, InputNumber } from 'antd'
import { formRule } from '@/utils/option.js'
import { dateOptions } from '@/utils/option.js'
import EditTable from '../editable'
import './index.scss'
const { TextArea } = Input
class MainSearch extends Component {
  static propTpyes = {
    dict: PropTypes.object, // 字典项
    formlist: PropTypes.any,
    card: PropTypes.object
  }
  state = {
    openType: null,
    resourceType: null,
    formlist: null
  }
  UNSAFE_componentWillMount () {
    let formlist = JSON.parse(JSON.stringify(this.props.formlist))
    let type = formlist.filter(cell => cell.key === 'type')[0].initVal
    let resourceType = formlist.filter(cell => cell.key === 'resourceType')[0].initVal
    let _options = ['label', 'field', 'initval', 'type', 'readonly', 'required'] // 默认显示项
    if ((type === 'multiselect' || type === 'select' || type === 'link') && resourceType === '0') { // 选择类型、自定义资源
      _options = [..._options, 'resourceType', 'options']
    } else if ((type === 'multiselect' || type === 'select' || type === 'link') && resourceType === '1') { // 选择类型、数据源
      _options = [..._options, 'resourceType', 'dataSource', 'valueField', 'valueText', 'orderBy', 'orderType']
    } else if (type === 'number') {
      _options = [..._options, 'decimal', 'min', 'max']
    } else if (type === 'fileupload') {
      _options = ['label', 'field', 'type', 'readonly', 'required']
    }
    if (type === 'select') {
      _options = [..._options, 'setAll', 'linkSubField']
    } else if (type === 'link') {          // 关联类型、增加关联字段
      _options = [..._options, 'setAll', 'linkField']
    } else if (type === 'funcvar') {       // 设置为函数变量时,不需要其他信息
      _options = ['label', 'field', 'type']
    }
    this.setState({
      openType: type,
      resourceType: resourceType,
      formlist: formlist.map(form => {
        if (dateOptions.hasOwnProperty(type) && form.key === 'initval') {
          form.options = dateOptions[type]
          form.type = 'select'
        } else if (type === 'number' && form.key === 'initval') {
          form.type = 'number'
          form.initVal = 0
        }
        form.hidden = !_options.includes(form.key)
        return form
      })
    })
  }
  componentDidMount () {
    const { card } = this.props
    if (card.focus) {
      try {
        let _form = document.getElementById('label')
        _form.select()
      } catch {
        console.warn('表单focus失败!')
      }
    }
  }
  openTypeChange = (key, value) => {
    if (key === 'type') {
      let _options = ['label', 'field', 'initval', 'type', 'readonly', 'required']
      if ((value === 'multiselect' || value === 'select' || value === 'link') && this.state.resourceType === '0') { // 选择类型、自定义资源
        _options = [..._options, 'resourceType', 'options']
      } else if ((value === 'multiselect' || value === 'select' || value === 'link') && this.state.resourceType === '1') { // 选择类型、数据源
        _options = [..._options, 'resourceType', 'dataSource', 'valueField', 'valueText', 'orderBy', 'orderType']
      } else if (value === 'number') {
        _options = [..._options, 'decimal', 'min', 'max']
      } else if (value === 'fileupload') {
        _options = ['label', 'field', 'type', 'readonly', 'required']
      }
      if (value === 'select') {
        _options = [..._options, 'setAll', 'linkSubField']
      } else if (value === 'link') {
        _options = [..._options, 'setAll', 'linkField']
      } else if (value === 'funcvar') {
        _options = ['label', 'field', 'type']
      }
      this.setState({
        openType: value,
        formlist: this.state.formlist.map(form => {
          form.hidden = !_options.includes(form.key)
          if (form.key === 'initval') {
            if (dateOptions.hasOwnProperty(value)) {
              form.options = dateOptions[value]
              form.type = 'select'
              form.initVal = ''
            } else if (value === 'number') {
              form.type = 'number'
              form.initVal = 0
            } else {
              form.type = 'text'
              form.initVal = ''
            }
            form.hidden = true
          }
          return form
        })
      }, () => {
        this.setState({
          formlist: this.state.formlist.map(form => {
            if (form.key === 'initval' && value !== 'fileupload' && value !== 'funcvar') {
              form.hidden = false
            }
            return form
          })
        })
      })
    }
  }
  onChange = (e, key) => {
    const { openType } = this.state
    let value = e.target.value
    if (key === 'resourceType') {
      let _options = ['label', 'field', 'initval', 'type', 'resourceType', 'readonly', 'required']
      if (value === '0') {
        _options = [..._options, 'options']
      } else if (value === '1') {
        _options = [..._options, 'dataSource', 'valueField', 'valueText', 'orderBy', 'orderType']
      }
      if (openType === 'select') {
        _options = [..._options, 'setAll', 'linkSubField']
      } else if (openType === 'link') {
        _options = [..._options, 'setAll', 'linkField']
      }
      this.setState({
        resourceType: value,
        formlist: this.state.formlist.map(form => {
          form.hidden = !_options.includes(form.key)
          return form
        })
      })
    }
  }
  getFields() {
    const { getFieldDecorator } = this.props.form
    const fields = []
    this.state.formlist.forEach((item, index) => {
      if (item.hidden) return
      if (item.type === 'text') { // 文本搜索
        let rules = []
        if (item.key === 'field') {
          rules = [{
            pattern: formRule.field.pattern,
            message: formRule.field.message
          }, {
            max: formRule.field.max,
            message: formRule.field.maxMessage
          }]
        } else {
          rules = [
            {
              max: formRule.input.max,
              message: formRule.input.message
            }
          ]
        }
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal || '',
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  },
                  ...rules
                ]
              })(<Input placeholder="" autoComplete="off" disabled={item.readonly} />)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'number') {
        if (item.key === 'decimal') {
          fields.push(
            <Col span={12} key={index}>
              <Form.Item label={item.label}>
                {getFieldDecorator(item.key, {
                  initialValue: item.initVal || 0,
                  rules: [
                    {
                      required: !!item.required,
                      message: this.props.dict['form.required.input'] + item.label + '!'
                    }
                  ]
                })(<InputNumber min={0} max={18} precision={0} />)}
              </Form.Item>
            </Col>
          )
        } else {
          fields.push(
            <Col span={12} key={index}>
              <Form.Item label={item.label}>
                {getFieldDecorator(item.key, {
                  initialValue: item.initVal,
                  rules: [
                    {
                      required: !!item.required,
                      message: this.props.dict['form.required.input'] + item.label + '!'
                    }
                  ]
                })(<InputNumber />)}
              </Form.Item>
            </Col>
          )
        }
      } else if (item.type === 'select') { // 下拉搜索
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal || '',
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <Select
                  showSearch
                  filterOption={(input, option) => option.props.children[2].toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  onChange={(value) => {this.openTypeChange(item.key, value)}}
                  getPopupContainer={() => document.getElementById('modal-fields-form-box')}
                >
                  {item.options.map(option =>
                    <Select.Option id={option.value} title={option.text} key={option.value} value={option.value}>
                      {item.key === 'icon' && <Icon type={option.text} />} {option.text}
                    </Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'multiselect') { // 多选
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal
              })(
                <Select
                  showSearch
                  mode="multiple"
                  filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                >
                  {item.options.map(option =>
                    <Select.Option id={option.uuid} key={option.uuid} value={option.field}>{option.label}</Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'radio') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <Radio.Group onChange={(e) => {this.onChange(e, item.key)}}>
                  {
                    item.options.map(option => {
                      return (
                        <Radio key={option.value} value={option.value}>{option.text}</Radio>
                      )
                    })
                  }
                </Radio.Group>,
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'textarea') {
        fields.push(
          <Col span={20} offset={4} key={index}>
            <Form.Item className="text-area">
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<TextArea rows={4} />)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'options') {
        fields.push(
          <Col span={20} offset={4} key={index}>
            <EditTable data={item.initVal} type={this.state.openType} ref="editTable"/>
          </Col>
        )
      }
    })
    return fields
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          let isvalid = true
          values.uuid = this.props.card.uuid
          // 下拉菜单或关联菜单
          if ((values.type === 'multiselect' || values.type === 'select' || values.type === 'link') && values.resourceType === '0') {
            values.options = this.refs.editTable.state.dataSource
            values.dataSource = ''
            let emptys = []
            if (values.type === 'multiselect' || values.type === 'select') {
              emptys = values.options.filter(op => !(op.Value && op.Text))
            } else {
              emptys = values.options.filter(op => !(op.Value && op.Text && op.ParentID))
            }
            if (emptys.length > 0) {
              isvalid = false
              notification.warning({
                top: 92,
                message: this.props.dict['header.form.selectItem.error'],
                duration: 10
              })
            }
          } else if ((values.type === 'multiselect' || values.type === 'select' || values.type === 'link') && values.resourceType === '1') {
            values.options = []
          } else if (values.type === 'funcvar') { // 函数变量为只读元素
            values.readonly = 'true'
          } else if (values.type === 'number' && (values.min || values.min === 0) && (values.max || values.max === 0)) { // 数值型验证最小最大值
            if (values.min > values.max) {
              isvalid = false
              notification.warning({
                top: 92,
                message: '最小值不可大于最大值!',
                duration: 10
              })
            }
          }
          if (isvalid) {
            resolve({
              type: 'search',
              values
            })
          }
        } else {
          reject(err)
        }
      })
    })
  }
  render() {
    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-fields-form" id="modal-fields-form-box">
        <Row gutter={24}>{this.getFields()}</Row>
      </Form>
    )
  }
}
export default Form.create()(MainSearch)
src/templates/formtabconfig/modalform/index.scss
New file
@@ -0,0 +1,16 @@
.ant-advanced-search-form.modal-fields-form {
  min-height: 180px;
  .ant-col-offset-4 {
    padding-left: 6px!important;
    padding-bottom: 20px;
  }
  .ant-form-item.text-area {
    margin-bottom: 0px;
    .ant-form-item-control-wrapper {
      width: 100%;
    }
  }
  .ant-input-number {
    width: 100%;
  }
}
src/templates/formtabconfig/source.jsx
@@ -214,38 +214,50 @@
  searchItems = [
    {
      type: 'search',
      label: '文本框',
      label: '文本',
      subType: 'text',
      url: ''
    },
    {
      type: 'search',
      label: '下拉框',
      label: '数字',
      subType: 'number',
      url: ''
    },
    {
      type: 'search',
      label: '下拉选择',
      subType: 'select',
      url: ''
    },
    {
      type: 'search',
      label: '时间框(天)',
      label: '文件上传',
      subType: 'fileupload',
      url: ''
    },
    {
      type: 'search',
      label: '时间(天)',
      subType: 'date',
      url: ''
    },
    {
      type: 'search',
      label: '时间框(周)',
      subType: 'dateweek',
      url: ''
    },
    {
      type: 'search',
      label: '时间框(月)',
      label: '时间(月)',
      subType: 'datemonth',
      url: ''
    },
    {
      type: 'search',
      label: '时间框(区间)',
      subType: 'daterange',
      label: '时间(秒)',
      subType: 'datetime',
      url: ''
    },
    {
      type: 'search',
      label: '多行文本',
      subType: 'textarea',
      url: ''
    }
  ]
src/templates/modalconfig/dragelement/card.jsx
@@ -8,7 +8,7 @@
const { MonthPicker } = DatePicker
const { TextArea } = Input
const Card = ({ id, card, moveCard, findCard, editCard, closeCard, hasDrop }) => {
const Card = ({ id, card, cols, moveCard, findCard, editCard, closeCard, hasDrop }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: ItemTypes.form, id, originalIndex },
@@ -55,15 +55,26 @@
      selectval = '全部'
    }
  }
  let labelCol = 'ant-col-sm-8'
  let wrapCol = 'ant-col-sm-16'
  if (card.type === 'textarea') {
    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'
    }
  }
  return (
    <div className="page-card" style={{ opacity: opacity}}>
      <div ref={node => drag(drop(node))}>
        {<div className="ant-row ant-form-item">
          <div className="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8">
          <div className={'ant-col ant-form-item-label ant-col-xs-24 ' + labelCol}>
            <label title={card.label}>{card.label}</label>
          </div>
          <div className="ant-col ant-form-item-control-wrapper ant-col-xs-24 ant-col-sm-16">
          <div className={'ant-col ant-form-item-control-wrapper ant-col-xs-24 ' + wrapCol}>
            {card.type === 'text' &&
              <Input style={{marginTop: '4px'}} defaultValue={card.initval} />
            }
@@ -83,7 +94,7 @@
              <DatePicker showTime defaultValue={card.initval ? moment().subtract(card.initval, 'days') : null} />
            }
            {card.type === 'textarea' &&
              <TextArea autoSize={{ minRows: 2, maxRows: 6 }} />
              <TextArea autosize={{ minRows: 2, maxRows: 6 }} />
            }
            {card.type === 'fileupload' &&
              <Button>
src/templates/modalconfig/dragelement/index.jsx
@@ -90,12 +90,8 @@
      const { index: overIndex } = findCard(`${targetId}`)
      let targetIndex = overIndex
      // if (!target) {
      targetIndex++
      // }
      // if (targetIndex < 0) {
      //   targetIndex = 0
      // }
      const _cards = update(cards, { $splice: [[targetIndex, 0, newcard]] })
      setCards(_cards)
@@ -115,9 +111,10 @@
  return (
    <div ref={drop} className="ant-row modal-fields-row">
      {cards.map(card => (
        <Col key={card.uuid} span={card.type !== 'textarea' ? _cols : 24}>
        <Col key={card.uuid} className={card.type === 'textarea' ? 'textarea' + setting.cols : ''} span={card.type !== 'textarea' ? _cols : 24}>
          <Card
            id={`${card.uuid}`}
            id={card.uuid}
            cols={setting.cols}
            card={card}
            moveCard={moveCard}
            editCard={editCard}
src/templates/modalconfig/dragelement/index.scss
@@ -15,4 +15,8 @@
}
.modal-fields-row {
  padding-bottom: 35px;
  .ant-col {
    padding-left: 12px;
    padding-right: 12px;
  }
}
src/templates/modalconfig/index.jsx
@@ -374,7 +374,7 @@
            text: this.state.dict['header.form.link']
          }, {
            value: 'fileupload',
            text: '文件上传'
            text: this.state.dict['header.form.fileupload']
          }, {
            value: 'date',
            text: this.state.dict['header.form.dateday']
@@ -386,10 +386,10 @@
            text: this.state.dict['header.form.datetime']
          }, {
            value: 'textarea',
            text: '多行文本'
            text: this.state.dict['header.form.textarea']
          }, {
            value: 'funcvar',
            text: '函数变量'
            text: this.state.dict['header.form.funcvar']
          }]
        },
        {
@@ -537,7 +537,7 @@
        {
          type: 'multiselect',
          key: 'linkSubField',
          label: '关联表单',
          label: this.state.dict['header.form.linkForm'],
          initVal: card.linkSubField || [],
          options: _inputfields
        }
@@ -902,7 +902,7 @@
            orderBy: '',
            orderType: 'asc',
            readonly: 'false',
            required: 'false'
            required: 'true'
          }
  
          items.push(newcard)
src/templates/modalconfig/index.scss
@@ -211,7 +211,6 @@
            display: flex;
            margin-bottom: 0px;
            .ant-form-item-label {
              // width: 100px;
              height: 40px;
              label {
                width: 100%;
@@ -223,7 +222,6 @@
              }
            }
            .ant-form-item-control-wrapper {
              flex: 1 1;
              .ant-select {
                width: 100%;
                margin-top: 4px;
@@ -245,20 +243,45 @@
                opacity: 0;
              }
            }
            .ant-col-cuslabel {
              width: 10.5%;
            }
            .ant-col-cuswrap {
              width: 89.5%;
            }
          }
          .edit {
            position: absolute;
            left: calc(33% - 100px);
            left: calc(33% - 70px);
            top: 0px;
            color: #1890ff;
            cursor: pointer;
            display: none;
          }
          .edit.close {
            left: calc(33% - 75px);
            left: calc(33% - 45px);
            color: #ff4d4f;
          }
        }
        .ant-col.textarea2 {
          padding-left: 7px;
          .page-card {
            .edit {
              left: calc(17% - 70px);
            }
            .edit.close {
              left: calc(17% - 45px);
            }
          }
        }
        .ant-col.textarea3 .page-card {
          .edit {
            left: calc(11% - 70px);
          }
          .edit.close {
            left: calc(11% - 45px);
          }
        }
        .page-card:hover {
          .edit {
            display: inline-block;
src/templates/tableshare/dragelement/index.jsx
@@ -9,6 +9,7 @@
const Container = ({list, setting, gridBtn, type, placeholder, handleList, handleMenu, deleteMenu, copyElement, profileMenu, handleGridBtn, showfield }) => {
  let target = null
  const [cards, setCards] = useState(list)
  const moveCard = (id, atIndex) => {
    const { card, index } = findCard(id)
@@ -78,43 +79,29 @@
        }
        
        newcard.label = 'label'
        newcard.field = ''
        newcard.initval = ''
        newcard.type = item.subType
        newcard.resourceType = '0'
        newcard.options = []
        newcard.dataSource = ''
        newcard.setAll = 'false'
        newcard.linkField = ''
        newcard.valueField = ''
        newcard.valueText = ''
        newcard.orderBy = ''
        newcard.orderType = 'asc'
        newcard.match = _match
        newcard.display = 'dropdown'
      } else if (item.type === 'action') {
        newcard.label = 'button'
        newcard.innerFunc = ''
        newcard.outerFunc = ''
        newcard.sql = ''
        newcard.sqlType = ''
        newcard.Ot = 'requiredSgl'
        newcard.OpenType = item.subType
        newcard.tabType = 'SubTable'
        newcard.linkTab = ''
        newcard.icon = ''
        newcard.class = 'default'
        newcard.intertype = 'inner'
        newcard.interface = ''
        newcard.method = 'POST'
        newcard.position = 'toolbar'
        newcard.execSuccess = 'grid'
        newcard.execError = 'never'
        newcard.popClose = 'never'
        newcard.errorTime = 15
        newcard.callbackFunc = ''
        newcard.pageTemplate = ''
        newcard.url = ''
        newcard.verify = null
        if (item.subType === 'excelIn' || item.subType === 'excelOut') {
@@ -144,12 +131,8 @@
      const { index: overIndex } = findCard(`${targetId}`)
      let targetIndex = overIndex
      // if (!target) {
      targetIndex++
      // }
      // if (targetIndex < 0) {
      //   targetIndex = 0
      // }
      const _cards = update(cards, { $splice: [[targetIndex, 0, newcard]] })
      setCards(_cards)
src/utils/option.js
@@ -25,7 +25,8 @@
    innerMessage: '内部函数名称只允许包含数字、字母和下划线,且以指定字符开始。'
  },
  textarea: {
    max: 1024
    max: 1024,
    message: '长文本最多1024个字符。'
  }
}