| | |
| | | import React, { Component } from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Table, Input, Popconfirm, Form, Icon, notification } from 'antd' |
| | | import { DndProvider, DragSource, DropTarget } from 'react-dnd' |
| | | import { Table, Input, Popconfirm, Form, notification, message } from 'antd' |
| | | import { PlusOutlined, EditOutlined, DeleteOutlined, SwapOutlined } from '@ant-design/icons' |
| | | |
| | | import Utils from '@/utils/utils.js' |
| | | import FileUpload from '@/tabviews/zshare/fileupload' |
| | | import asyncComponent from '@/utils/asyncComponent' |
| | | // import FileUpload from '@/tabviews/zshare/fileupload' |
| | | import './index.scss' |
| | | |
| | | const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent')) |
| | | const EditableContext = React.createContext() |
| | | let dragingIndex = -1 |
| | | |
| | | class BodyRow extends React.Component { |
| | | render() { |
| | | const { isOver, moveAble, connectDragSource, connectDropTarget, moveRow, ...restProps } = this.props |
| | | let { className } = restProps |
| | | |
| | | if (isOver && moveAble) { |
| | | if (restProps.index > dragingIndex) { |
| | | className += ' drop-over-downward' |
| | | } |
| | | if (restProps.index < dragingIndex) { |
| | | className += ' drop-over-upward' |
| | | } |
| | | } |
| | | |
| | | if (moveAble) { |
| | | return connectDragSource( |
| | | connectDropTarget(<tr {...restProps} className={className} style={{...restProps.style, cursor: 'move'}} />), |
| | | ) |
| | | } else { |
| | | return (<tr {...restProps} className={className} style={restProps.style} />) |
| | | } |
| | | } |
| | | } |
| | | |
| | | const rowSource = { |
| | | beginDrag(props) { |
| | | dragingIndex = props.index |
| | | return { |
| | | index: props.index, |
| | | } |
| | | } |
| | | } |
| | | |
| | | const rowTarget = { |
| | | drop(props, monitor) { |
| | | const dragIndex = monitor.getItem().index |
| | | const hoverIndex = props.index |
| | | |
| | | if (dragIndex === hoverIndex) { |
| | | return |
| | | } |
| | | |
| | | props.moveRow(dragIndex, hoverIndex) |
| | | |
| | | monitor.getItem().index = hoverIndex |
| | | }, |
| | | } |
| | | |
| | | const DragableBodyRow = DropTarget('row', rowTarget, (connect, monitor) => ({ |
| | | connectDropTarget: connect.dropTarget(), |
| | | isOver: monitor.isOver(), |
| | | }))( |
| | | DragSource('row', rowSource, connect => ({ |
| | | connectDragSource: connect.dragSource(), |
| | | }))(BodyRow), |
| | | ) |
| | | |
| | | class EditableCell extends Component { |
| | | getInput = (form) => { |
| | | const { inputType } = this.props |
| | | const { inputType, record } = this.props |
| | | if (inputType === 'file') { |
| | | return <FileUpload maxFile={1} fileType="picture-card"/> |
| | | return <SourceComponent initialValue={record ? (record.$url || '') : ''} type="" placement="right"/> |
| | | // return <FileUpload config={{ |
| | | // initval: record ? (record.$url || '') : '', |
| | | // suffix: '', |
| | | // maxfile: 1, |
| | | // fileType: 'picture-card' |
| | | // }}/> |
| | | } else { |
| | | return <Input onPressEnter={() => this.getValue(form)} /> |
| | | } |
| | |
| | | return |
| | | } |
| | | |
| | | if (row.$url && Array.isArray(row.$url)) { |
| | | if (!row.$url[0]) { |
| | | row.$url = '' |
| | | } else if (row.$url[0].origin) { |
| | | row.$url = row.$url[0].url || '' |
| | | } else if (!row.$url[0].origin && row.$url[0].status === 'done' && row.$url[0].response) { |
| | | row.$url = row.$url[0].response |
| | | } |
| | | } |
| | | this.props.onSave({...record, ...row}) |
| | | }) |
| | | } |
| | |
| | | _val = record[dataIndex] |
| | | } |
| | | |
| | | if (dataIndex === '$url' && _val) { |
| | | _val = [{ |
| | | uid: `10086`, |
| | | name: _val.slice(_val.lastIndexOf('/') + 1), |
| | | status: 'done', |
| | | url: _val, |
| | | origin: true |
| | | }] |
| | | } else if (dataIndex === '$url') { |
| | | _val = [] |
| | | } |
| | | |
| | | return ( |
| | | <td {...restProps}> |
| | | {editing ? ( |
| | | <Form.Item style={{ margin: 0 }}> |
| | | <Form.Item style={{ margin: '0 -5px 0 -5px' }}> |
| | | {getFieldDecorator(dataIndex, { |
| | | rules: [ |
| | | { |
| | | required: dataIndex === '$value', |
| | | message: `Please Input ${title}!`, |
| | | }, |
| | | ], |
| | | // rules: [ |
| | | // { |
| | | // required: dataIndex === '$value', |
| | | // message: `Please Input ${title}!`, |
| | | // }, |
| | | // ], |
| | | initialValue: _val, |
| | | })(this.getInput(form))} |
| | | </Form.Item> |
| | |
| | | |
| | | class EdiDataTable extends Component { |
| | | static propTpyes = { |
| | | dict: PropTypes.object, // 字典项 |
| | | type: PropTypes.object, // 数据类型,文本、图片 |
| | | transfield: PropTypes.object, // 字段名称 |
| | | type: PropTypes.string, // 是否为关联表单 |
| | | display: PropTypes.string, // 数据类型,文本、图片 |
| | | fields: PropTypes.array, // 字段集 |
| | | linkSubFields: PropTypes.array, // 填充字段 |
| | | onChange: PropTypes.func // 数据变化 |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | UNSAFE_componentWillReceiveProps (nextProps) { |
| | | if (!is(fromJS(this.props.fields), fromJS(nextProps.fields)) || !is(fromJS(this.props.type), fromJS(nextProps.type))) { |
| | | if ( |
| | | !is(fromJS(this.props.fields), fromJS(nextProps.fields)) || |
| | | !is(fromJS(this.props.linkSubFields), fromJS(nextProps.linkSubFields)) || |
| | | this.props.display !== nextProps.display || |
| | | (nextProps.multiple && this.props.multiple !== nextProps.multiple) || |
| | | this.props.type !== nextProps.type |
| | | ) { |
| | | this.setState({editingKey: ''}, () => { |
| | | this.setState({ |
| | | columns: this.getCloumns() |
| | |
| | | } |
| | | |
| | | getCloumns = () => { |
| | | const { type, fields } = this.props |
| | | const { display, fields, linkSubFields, transfield, type, multiple } = this.props |
| | | let columns = [] |
| | | let keys = ['ParentID', 'pid'] |
| | | |
| | | if (type === 'picture') { |
| | | if (display === 'picture') { |
| | | columns.push({ |
| | | title: 'url', |
| | | dataIndex: '$url', |
| | | inputType: 'file', |
| | | width: '40%', |
| | | // width: '40%', |
| | | editable: true, |
| | | render: (text) => { |
| | | if (!text) return '' |
| | | return <span style={{display: 'block', width: '70px', height: '70px'}}><img style={{width: '100%', height: '100%'}} src={text} alt="" /></span> |
| | | } |
| | | }) |
| | | } else { |
| | | columns = fields.map(item => ({ |
| | | } else if (display === 'color') { |
| | | columns.push({ |
| | | title: 'Color', |
| | | dataIndex: '$color', |
| | | inputType: 'text', |
| | | editable: true, |
| | | render: (text) => { |
| | | if (!text) return '' |
| | | return <div style={{height: '20px', background: text}}></div> |
| | | } |
| | | }) |
| | | } |
| | | |
| | | fields.forEach(item => { |
| | | keys.push(item.field) |
| | | columns.push({ |
| | | title: item.field, |
| | | dataIndex: item.field, |
| | | editable: true, |
| | | })) |
| | | }) |
| | | }) |
| | | |
| | | if (linkSubFields.length > 0) { |
| | | linkSubFields.forEach(m => { |
| | | if (keys.includes(m)) return |
| | | |
| | | columns.push({ |
| | | title: transfield[m] || m, |
| | | dataIndex: m, |
| | | editable: true, |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | columns.unshift({ |
| | |
| | | editable: true, |
| | | }) |
| | | |
| | | if (multiple === 'dropdown' && display === 'text') { |
| | | columns.unshift({ |
| | | title: 'pid', |
| | | dataIndex: 'pid', |
| | | editable: true, |
| | | }) |
| | | } |
| | | |
| | | if (type === 'link') { |
| | | columns.unshift({ |
| | | title: 'ParentID', |
| | | dataIndex: 'ParentID', |
| | | editable: true, |
| | | }) |
| | | } |
| | | |
| | | columns.push({ |
| | | title: 'operation', |
| | | title: '操作', |
| | | dataIndex: 'operation', |
| | | align: 'center', |
| | | width: '18%', |
| | | render: (text, record) => { |
| | | const { editingKey } = this.state |
| | |
| | | </span> |
| | | ) : ( |
| | | <div className={'operation-btn' + (editingKey !== '' ? ' disabled' : '')}> |
| | | <span className="primary" onClick={() => {editingKey === '' && this.edit(record.key)}}><Icon type="edit" /></span> |
| | | <span className="primary" onClick={() => {editingKey === '' && this.handleUpDown(record.key, 'up')}}><Icon type="arrow-up" /></span> |
| | | <span className="danger" onClick={() => {editingKey === '' && this.handleUpDown(record.key, 'down')}}><Icon type="arrow-down" /></span> |
| | | <span className="primary" onClick={() => {editingKey === '' && this.edit(record.key)}}><EditOutlined /></span> |
| | | <span className="hide-control" title="显示/隐藏" onClick={() => {editingKey === '' && this.handleHide(record.key)}}><SwapOutlined /></span> |
| | | {editingKey === '' ? <Popconfirm |
| | | overlayClassName="popover-confirm" |
| | | title={this.props.dict['model.query.delete']} |
| | | title="确定删除吗?" |
| | | onConfirm={() => this.handleDelete(record.key) |
| | | }> |
| | | <span className="danger"><Icon type="delete" /></span> |
| | | <span className="danger"><DeleteOutlined /></span> |
| | | </Popconfirm> : null} |
| | | {editingKey !== '' ? <span className="danger"><Icon type="delete" /></span> : null} |
| | | {editingKey !== '' ? <span className="danger"><DeleteOutlined /></span> : null} |
| | | </div> |
| | | ) |
| | | } |
| | |
| | | } |
| | | |
| | | onSave = (record) => { |
| | | const { type } = this.props |
| | | const newData = [...this.state.data] |
| | | const index = newData.findIndex(item => record.key === item.key) |
| | | |
| | | if (type === 'link') { |
| | | if (newData.filter(m => record.key !== m.key && record.$value === m.$value && record.ParentID === m.ParentID).length > 0) { |
| | | message.warning('相同ParentID下,此Value值已存在!') |
| | | } |
| | | } else { |
| | | if (newData.filter(m => record.key !== m.key && record.$value === m.$value).length > 0) { |
| | | message.warning('此Value值已存在!') |
| | | } |
| | | } |
| | | |
| | | if (index > -1) { |
| | | newData.splice(index, 1, record) |
| | | this.setState({ data: newData, editingKey: '' }, () => { |
| | |
| | | }) |
| | | } |
| | | |
| | | handleUpDown = (key, direction) => { |
| | | let _data = fromJS(this.state.data).toJS() |
| | | const index = _data.findIndex(item => key === item.key) |
| | | |
| | | if ((index === 0 && direction === 'up') || (index === _data.length - 1 && direction === 'down')) { |
| | | return |
| | | } |
| | | |
| | | if (direction === 'up') { |
| | | _data.splice(index - 1, 0, ..._data.splice(index, 1)) |
| | | } else { |
| | | _data.splice(index + 1, 0, ..._data.splice(index, 1)) |
| | | } |
| | | |
| | | this.setState({ |
| | | data: _data |
| | | }, () => { |
| | | this.props.onChange(_data) |
| | | }) |
| | | } |
| | | |
| | | save(form, key) { |
| | | const { type } = this.props |
| | | |
| | | form.validateFields((error, row) => { |
| | | if (error) { |
| | | return; |
| | | } |
| | | |
| | | if (row.$url && Array.isArray(row.$url)) { |
| | | if (!row.$url[0]) { |
| | | row.$url = '' |
| | | } else if (row.$url[0].origin) { |
| | | row.$url = row.$url[0].url || '' |
| | | } else if (!row.$url[0].origin && row.$url[0].status === 'done' && row.$url[0].response) { |
| | | row.$url = row.$url[0].response |
| | | const newData = [...this.state.data] |
| | | const index = newData.findIndex(item => key === item.key) |
| | | |
| | | if (type === 'link') { |
| | | if (newData.filter(m => key !== m.key && row.$value === m.$value && row.ParentID === m.ParentID).length > 0) { |
| | | message.warning('相同ParentID下,此Value值已存在!') |
| | | } |
| | | } else { |
| | | if (newData.filter(m => key !== m.key && row.$value === m.$value).length > 0) { |
| | | message.warning('此Value值已存在!') |
| | | } |
| | | } |
| | | |
| | | const newData = [...this.state.data] |
| | | const index = newData.findIndex(item => key === item.key) |
| | | if (index > -1) { |
| | | const item = newData[index] |
| | | newData.splice(index, 1, { |
| | |
| | | } |
| | | |
| | | handleAdd = () => { |
| | | const { fields, type } = this.props |
| | | if (this.state.editingKey) { |
| | | const { fields, display } = this.props |
| | | if (this.state.data.length >= 100) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: '请保存编辑中的元素!', |
| | | duration: 5 |
| | | }) |
| | | return |
| | | } else if (this.state.data.length >= 20) { |
| | | notification.warning({ |
| | | top: 92, |
| | | message: '最多可添加20项!', |
| | | message: '最多可添加100项!', |
| | | duration: 5 |
| | | }) |
| | | return |
| | | } |
| | | |
| | | let item = { key: Utils.getuuid(), $value: `${this.state.data.length + 1}` } |
| | | let item = { key: Utils.getuuid(), $value: `${this.state.data.length + 1}`, ParentID: '' } |
| | | |
| | | if (type === 'picture') { |
| | | if (display === 'picture') { |
| | | item.$url = '' |
| | | } else { |
| | | fields.forEach(f => { |
| | | item[f.field] = `${this.state.data.length + 1}` |
| | | }) |
| | | } else if (display === 'color') { |
| | | item.$color = '' |
| | | } |
| | | |
| | | fields.forEach(f => { |
| | | item[f.field] = `${this.state.data.length + 1}` |
| | | }) |
| | | |
| | | let data = [...this.state.data, item] |
| | | |
| | | this.setState({ data }, () => { |
| | | this.setState({ data, editingKey: '' }, () => { |
| | | this.props.onChange(data) |
| | | }) |
| | | } |
| | |
| | | this.setState({ editingKey: key }) |
| | | } |
| | | |
| | | handleHide = (key) => { |
| | | let _data = this.state.data.map(item => { |
| | | if (item.key === key) { |
| | | item.Hide = !item.Hide |
| | | } |
| | | return item |
| | | }) |
| | | this.setState({ |
| | | data: _data |
| | | }, () => { |
| | | this.props.onChange(_data) |
| | | }) |
| | | } |
| | | |
| | | moveRow = (dragIndex, hoverIndex) => { |
| | | const { editingKey } = this.state |
| | | let _data = fromJS(this.state.data).toJS() |
| | | |
| | | if (editingKey) return |
| | | |
| | | _data.splice(hoverIndex, 0, ..._data.splice(dragIndex, 1)) |
| | | |
| | | this.setState({ |
| | | data: _data |
| | | }, () => { |
| | | this.props.onChange(_data) |
| | | }) |
| | | } |
| | | |
| | | render() { |
| | | const { display, fields } = this.props |
| | | |
| | | const components = { |
| | | body: { |
| | | cell: EditableCell, |
| | | }, |
| | | row: DragableBodyRow, |
| | | cell: EditableCell |
| | | } |
| | | } |
| | | |
| | | const columns = this.state.columns.map(col => { |
| | |
| | | }) |
| | | |
| | | let addable = false |
| | | if (this.props.type === 'picture') { |
| | | if (display === 'picture' || display === 'color') { |
| | | addable = true |
| | | } else if (this.props.fields && this.props.fields.length > 0) { |
| | | } else if (fields && fields.length > 0) { |
| | | addable = true |
| | | } |
| | | |
| | | return ( |
| | | <EditableContext.Provider value={this.props.form}> |
| | | <div className="modal-card-data-table"> |
| | | {addable ? <Icon className="add-row" type="plus" onClick={this.handleAdd} /> : null} |
| | | <Table |
| | | components={components} |
| | | bordered |
| | | dataSource={this.state.data} |
| | | columns={columns} |
| | | rowClassName="editable-row" |
| | | pagination={false} |
| | | /> |
| | | {addable ? <PlusOutlined className="add-row" onClick={this.handleAdd} /> : null} |
| | | <DndProvider> |
| | | <Table |
| | | components={components} |
| | | bordered |
| | | rowKey="key" |
| | | dataSource={this.state.data} |
| | | columns={columns} |
| | | rowClassName={(record) => record.Hide ? 'editable-row hide' : 'editable-row'} |
| | | onRow={(record, index) => ({ |
| | | index, |
| | | moveAble: !this.state.editingKey, |
| | | moveRow: this.moveRow, |
| | | })} |
| | | pagination={false} |
| | | /> |
| | | </DndProvider> |
| | | </div> |
| | | </EditableContext.Provider> |
| | | ) |