king
2023-08-27 da64ab0923bf8817fc8599a6e37b953ce38f64c8
src/templates/zshare/editTable/index.jsx
@@ -2,18 +2,20 @@
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { DndProvider, DragSource, DropTarget } from 'react-dnd'
import { Table, Input, InputNumber, Popconfirm, Form, Icon, Select, Radio, Cascader, notification, message, Modal, Typography } from 'antd'
import { Table, Input, InputNumber, Popconfirm, Form, Select, Radio, Cascader, notification, message, Modal, Typography } from 'antd'
import { CopyOutlined, EditOutlined, DeleteOutlined, SwapOutlined } from '@ant-design/icons'
import Utils from '@/utils/utils.js'
import ColorSketch from '@/mob/colorsketch'
import PasteForm from '@/templates/zshare/pasteform'
import asyncComponent from '@/utils/asyncComponent'
import CusSwitch from './cusSwitch'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
let eTDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
const MkEditIcon = asyncComponent(() => import('@/components/mkIcon'))
const PasteBoard = asyncComponent(() => import('@/components/pasteboard'))
const EditableContext = React.createContext()
const { confirm } = Modal
let dragingIndex = -1
const { Paragraph } = Typography
@@ -76,14 +78,16 @@
class EditableCell extends Component {
  getInput = (form) => {
    const { inputType, options, min, max, unlimit } = this.props
    const { inputType, options, min, max, unlimit, allowClear } = this.props
    if (inputType === 'number' && unlimit) {
      return <InputNumber onPressEnter={() => this.getValue(form)} />
    } else if (inputType === 'number') {
      return <InputNumber min={min} max={max} precision={0} onPressEnter={() => this.getValue(form)} />
    } else if (inputType === 'color') {
      return <ColorSketch />
      return <ColorSketch allowClear={allowClear} />
    } else if (inputType === 'icon') {
      return <MkEditIcon allowClear/>
    } else if (inputType === 'switch') {
      return <CusSwitch />
    } else if (inputType === 'select') {
@@ -104,10 +108,12 @@
      )
    } else if (inputType === 'radio') {
      return (
        <Radio.Group>
        <Radio.Group style={{whiteSpace: 'nowrap'}}>
          {options.map((item, i) => (<Radio key={i} value={item.field || item.value}> {item.label || item.text} </Radio>))}
        </Radio.Group>
      )
    } else if (inputType === 'textarea') {
      return <Input.TextArea autoSize={true} placeholder=""/>
    } else {
      return <Input onPressEnter={() => this.getValue(form)}/>
    }
@@ -125,7 +131,7 @@
  renderCell = (form) => {
    const { getFieldDecorator } = form
    const { editing, dataIndex, title, record, children, className, required, inputType } = this.props
    const { editing, dataIndex, title, record, children, className, required, inputType, rules } = this.props
    return (
      <td className={className}>
@@ -135,8 +141,9 @@
              rules: [
                {
                  required: required,
                  message: ['number', 'text', 'input'].includes(inputType) ? `${eTDict['form.required.input']} ${title}!` : `${eTDict['form.required.select']} ${title}!`,
                }
                  message: ['number', 'text', 'input'].includes(inputType) ? `请输入${title}!` : `请选择${title}!`,
                },
                ...rules
              ],
              initialValue: inputType === 'multiStr' ? (record[dataIndex] ? record[dataIndex].split(',') : []) : record[dataIndex],
            })(this.getInput(form))}
@@ -156,6 +163,7 @@
class EditTable extends Component {
  static propTpyes = {
    actions: PropTypes.any,         // 操作项
    searchKey: PropTypes.any,       // 搜索条件
    data: PropTypes.any,            // 数据列表
    columns: PropTypes.array,       // 显示列
    onChange: PropTypes.func        // 数据变化
@@ -164,13 +172,23 @@
  state = {
    data: [],
    editingKey: '',
    visible: false,
    editLineId: '',
    columns: []
  }
  UNSAFE_componentWillMount () {
    const { data, actions } = this.props
    let columns = fromJS(this.props.columns).toJS()
    let operation = null
    let extra = null
    if (actions) {
      actions.forEach(item => {
        if (/^extra/.test(item)) {
          extra = item.split(':')
        }
      })
    }
    if (actions && (actions.includes('edit') || actions.includes('copy') || actions.includes('del'))) {
      let _operation = null
@@ -181,15 +199,14 @@
        return item.dataIndex !== 'operation'
      })
      let operation = {
      operation = {
        title: (<div>
          {eTDict['model.operation']}
          {actions.includes('copy') ? (
            <span className="copy-control">
              <Icon type="copy" onClick={() => this.copy()} />
              <Icon type="snippets" onClick={this.paste} />
            </span>
          ) : null}
          操作
          <span className="copy-control">
            {actions.includes('copy') ? <CopyOutlined title="复制" onClick={() => this.copy()} /> : null}
            {actions.includes('copy') ? <PasteBoard getPasteValue={this.pasteSubmit}/> : null}
            {actions.includes('clear') ? <DeleteOutlined title="清空" onClick={this.clear} /> : null}
          </span>
        </div>),
        dataIndex: 'operation',
        width: '140px',
@@ -201,24 +218,26 @@
              <EditableContext.Consumer>
                {form => (
                  <span onClick={() => this.save(form, record.uuid)} style={{ marginRight: 8 , color: '#1890ff', cursor: 'pointer'}}>
                    {eTDict['model.save']}
                    保存
                  </span>
                )}
              </EditableContext.Consumer>
              <span style={{ color: '#1890ff', cursor: 'pointer'}} onClick={() => this.cancel(record.uuid)}>{eTDict['model.cancel']}</span>
              <span style={{ color: '#1890ff', cursor: 'pointer'}} onClick={() => this.cancel(record.uuid)}>取消</span>
            </div>
          ) : (
            <div className={'edit-operation-btn' + (editingKey !== '' ? ' disabled' : '')} style={{minWidth: '110px'}}>
              {actions.includes('edit') ? <span className="primary" onClick={() => {editingKey === '' && this.edit(record.uuid)}}><Icon type="edit" /></span> : null}
              {actions.includes('copy') ? <span className="copy" onClick={() => {editingKey === '' && this.copy(record)}}><Icon type="copy" /></span> : null}
            <div className={'edit-operation-btn' + (editingKey !== '' ? ' disabled' : '')} style={{minWidth: '110px', whiteSpace: 'nowrap'}}>
              {actions.includes('edit') ? <span className="primary" title="编辑" onClick={() => {editingKey === '' && this.edit(record.uuid)}}><EditOutlined /></span> : null}
              {extra ? <span className="status" title={extra[2]} onClick={() => {editingKey === '' && this.handleStatus(record, extra[1])}}><SwapOutlined /></span> : null}
              {actions.includes('status') ? <span className="status" title="是否启用" onClick={() => {editingKey === '' && this.handleStatus(record, 'status')}}><SwapOutlined /></span> : null}
              {actions.includes('copy') ? <span className="copy" title="复制" onClick={() => {editingKey === '' && this.copy(record)}}><CopyOutlined /></span> : null}
              {actions.includes('del') && editingKey === '' ? <Popconfirm
                overlayClassName="popover-confirm"
                title={eTDict['model.query.delete']}
                title="确定删除吗?"
                onConfirm={() => this.handleDelete(record.uuid)
              }>
                <span className="danger"><Icon type="delete" /></span>
                <span className="danger"><DeleteOutlined /></span>
              </Popconfirm> : null}
              {actions.includes('del') && editingKey !== '' ? <span className="danger"><Icon type="delete" /></span> : null}
              {actions.includes('del') && editingKey !== '' ? <span className="danger"><DeleteOutlined /></span> : null}
            </div>
          )
        }
@@ -233,7 +252,7 @@
    this.setState({
      data: data || [],
      oricolumns: fromJS(this.props.columns).toJS(),
      operation,
      columns
    })
  }
@@ -241,26 +260,63 @@
  UNSAFE_componentWillReceiveProps (nextProps) {
    if (!is(fromJS(this.state.data), fromJS(nextProps.data))) {
      this.setState({data: nextProps.data, editingKey: ''})
    } else if (!is(fromJS(this.state.oricolumns), fromJS(nextProps.columns))) {
      let cols = {}
      nextProps.columns.forEach(col => {cols[col.dataIndex] = col})
      this.setState({
        oricolumns: fromJS(nextProps.columns).toJS(),
        columns: this.state.columns.map(col => {
          if (cols[col.dataIndex]) {
            return cols[col.dataIndex]
          }
          return col
    } else if (!is(fromJS(this.props.columns), fromJS(nextProps.columns))) {
      if (nextProps.columns.length === this.props.columns.length) {
        let cols = {}
        nextProps.columns.forEach(col => {cols[col.dataIndex] = col})
        this.setState({
          columns: this.state.columns.map(col => {
            if (cols[col.dataIndex]) {
              return cols[col.dataIndex]
            }
            return col
          })
        })
      })
      } else {
        let columns = fromJS(nextProps.columns).toJS()
        if (this.state.operation) {
          columns.push(this.state.operation)
        }
        this.setState({columns})
      }
    }
  }
  componentDidMount () {
    MKEmitter.addListener('editLineId', this.getEditLineId)
  }
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('editLineId', this.getEditLineId)
  }
  getEditLineId = (id) => {
    this.setState({ editLineId: id })
  }
  isEditing = record => record.uuid === this.state.editingKey
  cancel = () => {
    this.setState({ editingKey: '' })
  }
  clear = () => {
    const _this = this
    confirm({
      title: '确定清空列表吗?',
      content: '',
      onOk() {
        _this.setState({ data: [], editingKey: '' }, () => {
          _this.props.onChange([])
        })
      },
      onCancel() {}
    })
  }
  copy = (item) => {
@@ -283,8 +339,13 @@
    }
    try {
      let srcid = localStorage.getItem(window.location.href.split('#')[0] + 'srcId')
      if (srcid) {
        msg.$srcId = srcid
      }
      msg = window.btoa(window.encodeURIComponent(JSON.stringify(msg)))
    } catch {
    } catch (e) {
      console.warn('Stringify Failure')
      msg = ''
    }
@@ -300,27 +361,76 @@
    }
  }
  
  paste = () => {
    this.setState({visible: true})
  }
  pasteSubmit = () => {
  pasteSubmit = (res, callback) => {
    const { type } = this.props
    const { columns } = this.state
    let data = fromJS(this.state.data).toJS()
    this.pasteFormRef.handleConfirm().then(res => {
      if (res.key !== type) {
        message.warning('配置信息格式错误!')
        return
      }
    if (res.copyType === 'columns' && type === 'datasourcefield') {
      res.type = 'array'
      res.data = []
      res.columns.forEach(col => {
        if (!col.field) return
        if (col.type === 'number') {
          let datatype = 'Int'
          if (col.decimal) {
            datatype = `Decimal(18,${col.decimal})`
          }
      if (res.type === 'line') {
        let unique = true
        res.data.uuid = Utils.getuuid()
        columns.forEach(col => {
          if (col.unique !== true || !unique) return
          res.data.push({
            $index: res.data.length + 1,
            datatype: datatype,
            field: col.field,
            decimal: col.decimal,
            label: col.label,
            type: 'number',
            uuid: Utils.getuuid()
          })
        } else {
          let datatype = 'Nvarchar(50)'
          let fieldlength = 50
          if (col.fieldlength && [10, 20, 50, 100, 256, 512, 1024, 2048].includes(col.fieldlength)) {
            fieldlength = col.fieldlength
            datatype = `Nvarchar(${fieldlength})`
          }
          res.data.push({
            $index: res.data.length + 1,
            datatype: datatype,
            field: col.field,
            fieldlength: fieldlength,
            label: col.label,
            type: 'text',
            uuid: Utils.getuuid()
          })
        }
      })
    } else if (res.key !== type) {
      message.warning('配置信息格式错误!')
      return
    }
    if (res.type === 'line') {
      let unique = true
      res.data.uuid = Utils.getuuid()
      columns.forEach(col => {
        if (col.unique !== true || !unique) return
        if (col.uniqueFunc) {
          unique = col.uniqueFunc(data, res.data)
        } else if (col.strict) {
          let key = res.data[col.dataIndex].toLowerCase()
          let _index = data.findIndex(item => key === item[col.dataIndex].toLowerCase())
          if (_index > -1) {
            notification.warning({
              top: 92,
              message: col.title + '不可重复!',
              duration: 5
            })
            unique = false
          }
        } else {
          let _index = data.findIndex(item => res.data[col.dataIndex] === item[col.dataIndex])
          if (_index > -1) {
@@ -331,37 +441,66 @@
            })
            unique = false
          }
        })
        }
      })
        if (!unique) return
      if (!unique) return
        data.unshift(res.data)
        this.setState({ data, editingKey: '', visible: false }, () => {
          this.props.onChange(data)
        })
      } else if (res.type === 'array') {
        res.data.forEach(cell => {
          let unique = true
          cell.uuid = Utils.getuuid()
          columns.forEach(col => {
            if (col.unique !== true || !unique) return
      data.unshift(res.data)
      this.setState({ data, editingKey: '', editLineId: res.data.uuid || '' }, () => {
        this.props.onChange(data)
      })
    } else if (res.type === 'array') {
      res.data.forEach(cell => {
        let unique = true
        cell.uuid = Utils.getuuid()
        columns.forEach(col => {
          if (col.unique !== true || !unique) return
          if (col.uniqueFunc) {
            unique = col.uniqueFunc(data, cell)
          } else if (col.strict) {
            let _index = data.findIndex(item => cell[col.dataIndex].toLowerCase() === item[col.dataIndex].toLowerCase())
            if (_index > -1) {
              unique = false
            }
          } else {
            let _index = data.findIndex(item => cell[col.dataIndex] === item[col.dataIndex])
  
            if (_index > -1) {
              unique = false
            }
          })
          if (!unique) return
          data.push(cell)
          }
        })
        this.setState({ data, editingKey: '', visible: false }, () => {
          this.props.onChange(data)
        })
      }
      message.success('粘贴成功。')
        if (!unique) return
        data.push(cell)
      })
      this.setState({ data, editingKey: '' }, () => {
        this.props.onChange(data)
      })
    }
    callback()
    message.success('粘贴成功。')
  }
  handleStatus = (record, type) => {
    const { data } = this.state
    record[type] = record[type] === 'false' ? 'true' : 'false'
    let newData = data.map(item => {
      if (record.uuid === item.uuid) return record
      return item
    })
    this.setState({ data: newData }, () => {
      this.props.onChange(newData)
    })
  }
@@ -383,15 +522,32 @@
    columns.forEach(col => {
      if (col.unique !== true || !unique) return
      let _index = newData.findIndex(item => record.uuid !== item.uuid && record[col.dataIndex] === item[col.dataIndex])
      if (col.uniqueFunc) {
        unique = col.uniqueFunc(newData, record)
        return
      } else if (col.strict) {
        let key = record[col.dataIndex].toLowerCase()
        let _index = newData.findIndex(item => record.uuid !== item.uuid && key === item[col.dataIndex].toLowerCase())
      if (_index > -1) {
        notification.warning({
          top: 92,
          message: col.title + '不可重复!',
          duration: 5
        })
        unique = false
        if (_index > -1) {
          notification.warning({
            top: 92,
            message: col.title + '不可重复!',
            duration: 5
          })
          unique = false
        }
      } else {
        let _index = newData.findIndex(item => record.uuid !== item.uuid && record[col.dataIndex] === item[col.dataIndex])
        if (_index > -1) {
          notification.warning({
            top: 92,
            message: col.title + '不可重复!',
            duration: 5
          })
          unique = false
        }
      }
    })
@@ -422,31 +578,49 @@
      }
      const newData = [...this.state.data]
      const index = newData.findIndex(item => uuid === item.uuid)
      if (index > -1) {
        row = {...newData[index], ...row}
      } else {
        row.uuid = uuid
      }
      let unique = true
      columns.forEach(col => {
        if (col.unique !== true || !unique) return
        let _index = newData.findIndex(item => uuid !== item.uuid && row[col.dataIndex] === item[col.dataIndex])
        if (_index > -1) {
          notification.warning({
            top: 92,
            message: col.title + '不可重复!',
            duration: 5
          })
          unique = false
        if (col.uniqueFunc) {
          unique = col.uniqueFunc(newData, row)
          return
        } else if (col.strict) {
          let key = row[col.dataIndex].toLowerCase()
          let _index = newData.findIndex(item => row.uuid !== item.uuid && key === item[col.dataIndex].toLowerCase())
          if (_index > -1) {
            notification.warning({
              top: 92,
              message: col.title + '不可重复!',
              duration: 5
            })
            unique = false
          }
        } else {
          let _index = newData.findIndex(item => row.uuid !== item.uuid && row[col.dataIndex] === item[col.dataIndex])
          if (_index > -1) {
            notification.warning({
              top: 92,
              message: col.title + '不可重复!',
              duration: 5
            })
            unique = false
          }
        }
      })
      if (!unique) return
      if (index > -1) {
        const item = newData[index]
        newData.splice(index, 1, {
          ...item,
          ...row,
        })
        newData.splice(index, 1, row)
        this.setState({ data: newData, editingKey: '' }, () => {
          this.props.onChange(newData)
        })
@@ -479,7 +653,8 @@
  }
  render() {
    const { actions } = this.props
    const { actions, indexShow, searchKey } = this.props
    const { editLineId } = this.state
    let components = {
      body: {
@@ -488,13 +663,13 @@
    }
    let moveprops = {}
    if (actions.includes('move')) {
    if (actions.includes('move') && !searchKey) {
      components.body.row = DragableBodyRow
      moveprops.moveAble = !this.state.editingKey
      moveprops.moveRow = this.moveRow
    }
    
    const columns = this.state.columns.map(col => {
    let  columns = this.state.columns.map(col => {
      if (col.copy) {
        col.render = (text) => (<Paragraph copyable>{text}</Paragraph>)
      }
@@ -506,16 +681,35 @@
          inputType: col.inputType,
          dataIndex: col.dataIndex,
          options: col.options || [],
          rules: col.rules || [],
          min: col.min || 0,
          max: col.max || 500,
          unlimit: col.unlimit,
          required: col.required !== false ? true : false,
          allowClear: col.allowClear === true,
          title: col.title,
          editing: this.isEditing(record),
          onSave: this.onSave,
        }),
      }
    })
    if (indexShow !== false) {
      columns.unshift({
        title: '序号',
        dataIndex: '$index',
        className: 'mk-index',
        width: '60px',
      })
    }
    const data = this.state.data.map((item, index) => {
      item.$index = index + 1
      return item
    })
    let reg = searchKey ? new RegExp(searchKey, 'i') : null
    return (
      <EditableContext.Provider value={this.props.form}>
@@ -525,9 +719,20 @@
              bordered
              rowKey="uuid"
              components={components}
              dataSource={this.state.data}
              dataSource={data}
              columns={columns}
              rowClassName="editable-row"
              rowClassName={record => {
                let className = 'editable-row'
                if (editLineId && editLineId === record.uuid) {
                  className += ' active'
                }
                if (searchKey) {
                  if (!reg.test(record.field) && !reg.test(record.label)) {
                    className += ' hidden'
                  }
                }
                return className
              }}
              pagination={false}
              onRow={(record, index) => ({
                index,
@@ -535,18 +740,6 @@
              })}
            />
          </DndProvider>
          {/* 信息粘贴 */}
          <Modal
            title={eTDict['header.form.paste']}
            visible={this.state.visible}
            width={600}
            maskClosable={false}
            onOk={this.pasteSubmit}
            onCancel={() => {this.setState({visible: false})}}
            destroyOnClose
          >
            <PasteForm dict={eTDict} wrappedComponentRef={(inst) => this.pasteFormRef = inst}/>
          </Modal>
        </div>
      </EditableContext.Provider>
    )